一. 原型模式简介
原型模式(Prototype Pattern)也是一种创建型模式,它关注的是大量相似对象的创建问题。我们经常会遇到这样的情况:在系统中要创建大量的对象,这些对象之间具有几乎完全相同的功能,只是在细节上有一点儿差别。
这样的情形经常遇到。三国系列游戏是我最喜欢的游戏系列之一。你有没有注意到那里边上百位英雄的头像基本上很相似?你仔细区分就会发现,虽然每个人都不同,但基本上只具有几种脸型:长方的、圆形的、细长的,然后配上不同的胡子、眉毛、眼睛、嘴,有的再加点儿伤疤或装饰物(比如给独眼龙夏侯敦加个单眼罩),就成了不同的人物头像!那么,为什么会这样的?因为根据研究表明,人类的脸谱基本上只有有限的几个类型,只不过在细节和组合方面存在些许差异。游戏制作者具有依据这个理论,只对人脸进行有限的几种建模,然后再通过组合、修饰,就可以产生无数的头像了。
原型模式(Prototype Pattern)也是一种创建型模式,它关注的是大量相似对象的创建问题。我们经常会遇到这样的情况:在系统中要创建大量的对象,这些对象之间具有几乎完全相同的功能,只是在细节上有一点儿差别。
这样的情形经常遇到。三国系列游戏是我最喜欢的游戏系列之一。你有没有注意到那里边上百位英雄的头像基本上很相似?你仔细区分就会发现,虽然每个人都不同,但基本上只具有几种脸型:长方的、圆形的、细长的,然后配上不同的胡子、眉毛、眼睛、嘴,有的再加点儿伤疤或装饰物(比如给独眼龙夏侯敦加个单眼罩),就成了不同的人物头像!那么,为什么会这样的?因为根据研究表明,人类的脸谱基本上只有有限的几个类型,只不过在细节和组合方面存在些许差异。游戏制作者具有依据这个理论,只对人脸进行有限的几种建模,然后再通过组合、修饰,就可以产生无数的头像了。
用面向对象的方法来说就是,我们先建立一个原型,然后通过对原型进行复制和修饰的方法,就可以产生一个和原型相似的新对象。用GoF的话来说就是:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
二. 原型模式实例
前几天,我很不幸把屋门的钥匙给弄丢了,结果进不了家门。万幸的是,GF那儿还有一把,于是第二天我拿了她的那把去配钥匙。另外,她还让我顺便给她配一把橱柜的钥匙。现在配个钥匙真是简单,把钥匙给他,他直接找一个合适的钥匙胚子,把我的钥匙夹在配钥匙机的一端,胚子夹在另一端,一开电源,一把标尺比着我的钥匙齿型走一遍,砂轮就在胚子上复制出一把钥匙来!一分钟不到,两把新钥匙就搞定了!
用OO来描述,我的
旧钥匙是一个原型。配钥匙的过程就是根据我提供的原型,再复制一份出来,就有了一个新的钥匙。两个钥匙完全一样,我也可以给新配的钥匙贴个标签,以表明是“我的”。用这样的方法,我可以对各种钥匙进行复制,也可以复制无限多份。OO分析的类图如下:
结构
理解
1.
Prototype
是原型基类,提供Clone
纯虚方法,它根据不同的派生类来克隆不同的对象。
2.
ConcretePrototype
是原型具体类。实现Clone
方法,克隆自己,返回克隆后的新对象。
3.
Client
调用基类Clone
接口,就可以得到一个克隆对象。
要点
1.
原型模式中,Client
并不知道要克隆对象的实际类型,只需知道基类类型即可。
2.
克隆对象比直接创建对象的优点在于,克隆是将原有对象的行为属性带到了新的对象中。
3.
C++
没有克隆方法,要克隆一个对象,需要借助拷贝构造函数(Copy Constructor
)来实现。拷贝构造函数中实现拷贝对象有浅拷贝和深拷贝:
浅拷贝
是指对象复制时,只是对于对象中的数据成员进行值拷贝;深拷贝是指对象赋值时,对于对象的简单数据成员进行值拷贝,对于对象中的动态成员(堆或者其他系统资源),要重新分配动态空间。
当类不定义拷贝构造函数的时候,编译器会自动生一个构造函数,叫做默认拷贝构造函数。默认拷贝构造函数使用浅拷贝方式。如果类中含有动态数据成员,就必须使用深拷贝方式实现拷贝构造函数,否则,在销毁对象时,两个对象的析构函数将对同一个内存空间释放两次,产生运行时错误。
应用
源码中,Line
是原型基类,具体克隆类是直线和曲线。
源码
#include
<iostream>
#include
<string>
using
namespace std;
//
原型基类
.
线条
class
CLinePrototype
{
public
:
virtual ~CLinePrototype(){};
virtual CLinePrototype* Clone() = 0;
};
//
原型具体类
.
直线
class
CBeelinePrototype : public CLinePrototype
{
public
:
CBeelinePrototype(){};
//
拷贝构造函数
CBeelinePrototype(const CBeelinePrototype &o)
{
this->m_iWeight = o.m_iWeight;
}
virtual ~CBeelinePrototype(){};
virtual CLinePrototype* Clone()
{
cout << "clone a line" << endl;
return new CBeelinePrototype(*this);
}
void SetWeight(const int &iWeight)
{
this->m_iWeight = iWeight;
}
void Print()
{
cout << "beeline weight: " << m_iWeight << endl;
}
protected
:
int m_iWeight;
};
//
原型具体类
.
曲线
class
CCurvePrototype : public CLinePrototype
{
public
:
CCurvePrototype(){};
//
拷贝构造函数
CCurvePrototype(const CCurvePrototype &o)
{
this->m_strType = o.m_strType;
}
virtual ~CCurvePrototype(){};
virtual CLinePrototype* Clone()
{
cout << "clone a curve" << endl;
return new CCurvePrototype(*this);
}
void SetType(const string &m_strType)
{
this->m_strType = m_strType;
}
void Print()
{
cout << "beeline type: " << m_strType.c_str() << endl;
}
protected
:
string m_strType;
};
//Client
CLinePrototype
* Clone(CLinePrototype* pPrototype)
{
return pPrototype->Clone();
}
int
main()
{
CBeelinePrototype* p1 = new CBeelinePrototype();
p1->SetWeight(10);
CLinePrototype* p1Clone = Clone(p1);
CBeelinePrototype* p1Clone1 = dynamic_cast<CBeelinePrototype*>(p1Clone);
p1Clone1->Print();
CCurvePrototype* p2 = new CCurvePrototype();
p2->SetType("DOT");
CLinePrototype* p2Clone = Clone(p2);
CCurvePrototype* p2Clone1 = dynamic_cast<CCurvePrototype*>(p2Clone);
p2Clone1->Print();
system("pause");
return 0;
}
输出:
clone a line
beeline weight: 10
clone a curve
beeline type: DOT