prototype模式通过实例对象指定需要创建的类型,这与上一篇我们提到的factory method模式有本质不同,factory method模式是通过类的继承定义不同子类来达到创建不同类型对象的目的,属于类模式,prototype模式通过调用组合的对象成员生成不同类型的对象实例,属于对象模式。
由于这个特性,prototype具有以下适用场合:
· 需要运行时确定实例化的类时,比如动态装载库时
· 避免创建过多子类时。子类太多永远是不受欢迎的,在factory method中我们也提到通过模板或者参数化来减少子类数目。
· 实例化对象的状态组合过多时,先建立原型库,使用时通过注册表取得相应原型来生成目标对象会更加方便。
prototype的优点包括:
· 运行时增加和删除产品
· 改变对象,这点是组合模式的共同优点
· 减少子类的数目
· 用类动态配置应用
prototype的最大缺点应该就是在某些语言中实现clone操作可能会很困难,特别是包含循环引用的额情况下。
下面继续使用上一篇文章中的例子,开发一个芯片设计软件,用prototype实现。所有的图形继承自MaskFigure,必须实现clone函数,MaskDesigner初始化时传入对象原型指针,MakeFigure时通过原型的clone生成新的图形。Factory method是通过图形对应的类调用图形类的构造函数生成新的对象,通俗点讲,就好像factory method是用设计图纸重新画了个图形出来,而prototype是临摹已经画好了的图形。类结构如下:
代码实现如下:
//mask.hpp #ifndef MASK_HPP #define MASK_HPP class MaskFigure{ public: virtual ~MaskFigure()=0; virtual MaskFigure* clone()=0; protected: MaskFigure(); MaskFigure(const MaskFigure&); }; class MaskRound:public MaskFigure { public: MaskRound(); MaskRound(const MaskRound&); MaskRound* clone(); ~MaskRound(); }; class MaskRec:public MaskFigure { public: MaskRec(); MaskRec(const MaskRec&); MaskRec* clone(); ~MaskRec(); }; class MaskTri:public MaskFigure { public: MaskTri(); MaskTri(const MaskTri&); MaskTri* clone(); ~MaskTri(); }; #endif //mask.cpp #include <iostream> #include "mask.hpp" using std::cout; using std::endl; MaskFigure::MaskFigure() { cout<<"init MaskFigure"<<endl; } MaskFigure::MaskFigure(const MaskFigure& mf) { cout<<"copy Figure"<<endl; } MaskFigure::~MaskFigure() { cout<<"delete MaskFigure"<<endl; } MaskRound::MaskRound() { cout<<"Draw roundness on Mask"<<endl; } MaskRound::MaskRound(const MaskRound& mr) :MaskFigure(mr){ cout<<"copy roundness"<<endl; } MaskRound* MaskRound::clone() { return new MaskRound(*this); } MaskRound::~MaskRound() { cout<<"delete MaskRound"<<endl; } MaskRec::MaskRec() { cout<<"Draw rectangle on Mask"<<endl; } MaskRec::MaskRec(const MaskRec& mr) :MaskFigure(mr){ cout<<"copy rectangle"<<endl; } MaskRec* MaskRec::clone() { return new MaskRec(*this); } MaskRec::~MaskRec() { cout<<"delete MaskRec"<<endl; } MaskTri::MaskTri() { cout<<"Draw triangle on Mask"<<endl; } MaskTri::MaskTri(const MaskTri& mt) :MaskFigure(mt){ cout<<"copy triangle"<<endl; } MaskTri* MaskTri::clone() { return new MaskTri(*this); } MaskTri::~MaskTri() { cout<<"delete MaskTri"<<endl; } //maskdesigner.hpp #ifndef FIGUREDESIGNER_HPP #define FIGUREDESIGNER_HPP #include "mask.hpp" class FigureDesigner{ public: FigureDesigner(MaskFigure *mf){ figure = mf; } MaskFigure* MakeFigure(){ return figure->clone(); } private: MaskFigure *figure; }; #endif //main.cc #include <memory> #include <iostream> #include "maskdesigner.hpp" using std::cout; using std::endl; using std::shared_ptr; int main() { MaskRound mro; MaskRec mre; MaskTri mtr; FigureDesigner md1(&mro); shared_ptr<MaskFigure> mfptr1(md1.MakeFigure()); FigureDesigner md2(&mre); shared_ptr<MaskFigure> mfptr2(md2.MakeFigure()); FigureDesigner md3(&mtr); shared_ptr<MaskFigure> mfptr3(md3.MakeFigure()); }
讲到prototype模式,有必要提一下c++ Covariance特性,即c++的协变性质,在虚函数实现时,一般要求派生类重写的虚函数必须与基类函数有一致的返回值类型,参数个数及类型。
不过这也有个特例,如果返回值类型满足下面条件时可以不同,
1. 基类和派生类虚函数返回值类型都是指向类的指针或者引用
2. 基类虚函数返回值指针或引用所指向的类类型是派生类返回值指针或引用所指向的类类型的间接或直接基类,即属于同一个继承体系。
这表示如下情况是允许的,
class Base { /* ... */ }; class Derived: public Base { /* ... */ }; class B { virtual Base* func() { return new Base; } virtual ~B() { } }; class D: public B { Derived* func() { return new Derived; } virtual ~D() { } };
fanc的返回值分别是base*和Derived*,属于同一继承体系内。但返回值换成其他类型如基本类型时则不允许。这一特性在prototype中得到充分的应用, MaskFigure的clone函数返回 MaskFigure*指针类型,MaskRound,MaskRec和MaskTri的clone函数分别返回MaskRound*,MaskRec*和MaskTri*指针类型。
(完)