一 摘要
本章主要说明原型模式,该设计模式主要意图是:用原型实例指定创建对象,并且通过拷贝这些原型创建新的对象。 简单地说就是克隆一个和自己相同的对象。
举一个最简单的例子来说明原型模式:英雄联盟有个英雄叫做小丑,小丑可以放置分身,但是这个肯定不能创建,要不就有两个小丑了,这时我们可以从本身克隆出一个。
为什么要使用原型模式?
原型模式和建造者模式、工厂方法模式一样,都属于创建型模式的一种。简单的来说,我们使用原型模式,就是为了创建对象。但是,在以下场景下,使用原型模式是最好的选择:
当我们的对象类型不是开始就能确定的,而这个类型是在运行期确定的话,那么我们通过这个类型的对象克隆出一个新的对象比较容易一些;有的时候,我们需要一个对象在某个状态下的副本,此时,我们使用原型模式是最好的选择;例如:一个对象,经过一段处理之后,其内部的状态发生了变化;这个时候,我们需要一个这个状态的副本,如果直接new一个新的对象的话,但是它的状态是不对的,此时,可以使用原型模式,将原来的对象拷贝一个出来,这个对象就和之前的对象是完全一致的了;当我们处理一些比较简单的对象时,并且对象之间的区别很小,可能就几个属性不同而已,那么就可以使用原型模式来完成,省去了创建对象时的麻烦了;
二. UML类图
主要参与者
该设计模式的参与者有三个,分别是:
- Prototype 抽象原型,克隆自生的抽象接口;
- ConcretePrototype 具体原型实现,实现克隆自生各个部件的具体实现接口;
- Client 用户,让一个原型克隆自生从而创建一个新的对象;
三 例子
//相当于类图中的Prototype,也就是原型
class Hero
{
public:
Hero() {};
Hero(int * attack, int * blood) :m_pAttack(attack), m_pBlood(blood) {}
Hero(const Hero & hero)//重写拷贝构造
{
m_pAttack = new int(*hero.m_pAttack);
m_pBlood = new int(*hero.m_pBlood);
}
void show()
{
cout << "攻击力:" << *m_pAttack << "\t血量:" << *m_pBlood << endl;
}
virtual Hero* Clone() = 0;
~Hero()
{
if (m_pAttack)
{
delete m_pAttack;
m_pAttack = NULL;
}
if (m_pBlood)
{
delete m_pBlood;
m_pBlood = NULL;
}
}
protected:
int* m_pAttack = NULL;//用指针主要是上面拷贝构造-深拷贝
int* m_pBlood = NULL;
};
//相当于类图中的ConcretePrototype
class Joker :public Hero
{
public:
Joker(int *attack, int* blood) :Hero(attack, blood) {}
Joker(const Joker &joker) :Hero(joker) {}//调用父类拷贝构造
Hero* Clone()
{
Joker* joker = new Joker(*this);
//调用拷贝构造后,可以自己对属性设置
*joker->m_pAttack = (*m_pAttack/2);
*joker->m_pBlood = (*m_pBlood /2);
return joker;
}
};
//相当于类图中的Client
int main()
{
Hero *joker = new Joker(new int(100), new int(100));
Hero *fake = joker->Clone();
joker->show();
fake->show();
delete joker;
delete fake;
getchar();
return 0;
}
浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用 深拷贝:
创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”
四 优缺点
优点
- 方便在运行时刻增加和删除产品;
- 改变值以指定新的对象;
- 改变结构以指定新的对象;
- 减少子类的构造;
- 用类动态配置应用;
五 总结
依我的目前理解,其实就是一个特殊的拷贝构造