本文记录原型设计模式。下面举例时,还是以游戏打怪为例给出例子。
原型模式使用场景
**原型模式:**通过一个对象克隆出多个一模一样的对象。
**使用场景:**打BOSS到一半血时,BOSS分裂为两个半血的BOSS。
对象的克隆需要调用类的拷贝构造函数,因为如果直接new 一个类,new出来的是新类,满血的BOSS类,所以new半血的BOSS类,只能通过拷贝构造函数来new;
cpp
class CMonster
{
public:
CMonster(int left, int magic, int attack)
:m_life(left)
, m_magic(magic)
, m_attack(attack)
{}
// 原型模式的克隆函数,使用拷贝构造函数实现
CMonster(const CMonster& obj)
{
cout << "CMonster::CMonster(const CMonster& obj)" << endl;
m_life = obj.m_life;
m_magic = obj.m_magic;
m_attack = obj.m_attack;
}
virtual ~CMonster()
{
cout << "CMonster::~CMonster()" << endl;
}
// 克隆函数
virtual CMonster* clone() = 0;
// 减少生命值 魔法值,攻击力
virtual void reduce() = 0;
// 输出当前的生命值、魔法值和攻击力
virtual void printStatus() const = 0;
protected:
int m_life; //生命值
int m_magic; //魔法值
int m_attack; //攻击力
};
// 实现三个怪物类
class CUndead : public CMonster
{
public:
CUndead(int left, int magic, int attack)
: CMonster(left, magic, attack)
{
}
CUndead(const CUndead& obj)
: CMonster(obj)
{
cout << "CUndead::CUndead(const CUndead& obj)" << endl;
}
// 克隆函数
virtual CMonster* clone() override
{
return new CUndead(*this);// 调用拷贝构造函数
}
// 减少生命值 魔法值,攻击力
virtual void reduce() override
{
m_life -= 10; // 假设每次减少10点生命值
m_magic -= 5; // 假设每次减少5点魔法值
m_attack -= 2; // 假设每次减少2点攻击力
}
virtual void printStatus() const override
{
cout << "Undead Status: Life = " << m_life
<< ", Magic = " << m_magic
<< ", Attack = " << m_attack << endl;
}
};
class CElement : public CMonster
{
public:
CElement(int left, int magic, int attack)
: CMonster(left, magic, attack)
{
}
// 调用拷贝构造函数
CElement(const CElement& obj)
: CMonster(obj)
{
}
// 克隆函数原理:每次克隆,new CElement(*this);先会调用拷贝构造函数
// 在拷贝构造函数中,调用了父类CMonster的拷贝构造函数,然后完成了成员变量的拷贝
// 如果没有父类,则直接在本类的拷贝构造函数中完成成员变量的赋值操作
virtual CMonster* clone() override
{
return new CElement(*this);
}
// 减少生命值 魔法值,攻击力
virtual void reduce() override
{
m_life -= 20; // 假设每次减少10点生命值
m_magic -= 15; // 假设每次减少5点魔法值
m_attack -= 22; // 假设每次减少2点攻击力
}
// 输出状态
virtual void printStatus() const override
{
cout << "Undead Status: Life = " << m_life
<< ", Magic = " << m_magic
<< ", Attack = " << m_attack << endl;
}
};
void test()
{
// 创建一个亡灵类怪物
CMonster* undead = new CUndead(100, 50, 30);
undead->printStatus();
// Undead Status: Life = 100, Magic = 50, Attack = 30
undead->reduce();
undead->printStatus();
// Undead Status: Life = 90, Magic = 45, Attack = 28
// 克隆一个亡灵类怪物
CMonster* undeadClone = undead->clone();
undeadClone->printStatus();
// Undead Status: Life = 90, Magic = 45, Attack = 28
}
克隆函数原理:基于现在的对象数据,拷贝一份新的对象。每次克隆,new CElement(*this);先会调用拷贝构造函数,在拷贝构造函数中,调用了父类CMonster的拷贝构造函数,然后完成了成员变量的拷贝。如果没有父类,则直接在本类的拷贝构造函数中完成成员变量的赋值操作
原型模式定义
用原型实例制定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式的两种角色,
Prototype(抽象原型类):CMonster类;
ConcretePrototype具体原型类,CEle CUndead 。
如果对象内部数据比较复杂多变,并且在创建对象的时候希望保持对象的当前状态,那么用原型模式显然比用工厂方法模式更合适。比如:BOSS的血量不断变化, 需要复制当前的BOSS,此时用原型更方便。
工厂方法模式和原型模式创建对象的共同点
1 都不需要知道所储行间对象所属的类名;工厂模式直接使用工厂来复制,原型模式直接使用clone()方法来复制。
2 工厂方法模式中的createMonster仍旧属于根据类名类生成的新对象;
3 原型模式中的clone() 是生成的当前对象的状态。
原型模式优缺点
1 如果创建新对象内部数据比较复杂多变,原型模式创建对象的效率可能会更高。
2 原型模式不存在额外的等级结构---原型模式不需要额外的工厂类;
3 clone() 接口的实现方法有多种,不用非要在clone()种拷贝构造。
4 有些情况下,产品类中存在一个克隆方法也会给开发提供一些便利。
使用全局方法
cpp
// 使用全局方法创建CMonster
void Glb_CreateMonster(CMonster *pMonster)
{
CMonster *p = nullptr;
if (dynamic_cast<CUndead *> (pMonster) != nullptr)
{
cout << "这是CUndead 类型" << endl;
}
else if (dynamic_cast<CEle *>(pMonster) != nullptr)
{
cout << "这是CEle 类型" << endl;
}
}
原型模式UML
