意图:
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。这个其实和C++的拷贝构造函数的作用是一致的,实际上就是动态抽取当前对象运行时的状态。
UML图:
客户(Client)角色:让一个原型克隆自身从而创建一个新的对象。
抽象原型(Prototype)角色:这是一个抽象角色,抽象类实现。此角色声明一个克隆自身的接口。
具体原型(Concrete Prototype)角色:实现一个克隆自身的操作。
适用性
1.当一个系统应该独立于它的产品创建、构成和表示时;
2.当要实例化的类是在运行时指定时(如动态装载);
3.为了避免创建一个与产品类层次平行的工厂类层次时;
4.当一个类的实例只能有几个不同状态组合中的一种时。
例子:
//prototype抽象类,定义了Clone接口 #ifndef _RESUME_H #define _RESUME_H #include <iostream> class Resume { protected: char *name; public: Resume(){} virtual ~Resume(){} virtual Resume *Clone() { return NULL; } virtual void Show(){} }; #endif //ConcretePrototype,实现了Clone接口 #ifndef _RESUME_A_H #define _RESUME_A_H #include "Resume.h" #include <iostream> using namespace std; class ResumeA : public Resume { public: ResumeA(const char *str) { if (str==NULL) { name = new char[1]; name[0] = '\0'; } else { name = new char[strlen(str)+1]; strcpy(name, str); } } ~ResumeA() { delete[] name; } ResumeA(const ResumeA &r) { name = new char[strlen(r.name)+1]; strcpy(name, r.name); } virtual Resume* Clone() { return new ResumeA(*this); } virtual void Show() { cout<<"ResumeA:"<<name<<endl; } }; #endif //ConcretePrototype,实现了Clone接口 #ifndef _RESUME_B_H #define _RESUME_B_H #include "Resume.h" #include <iostream> using namespace std; class ResumeB : public Resume { public: ResumeB(const char *str) { if (str==NULL) { name = new char[1]; name[0] = '\0'; } else { name = new char[strlen(str)+1]; strcpy(name, str); } } ~ResumeB() { delete[] name; } ResumeB(const ResumeB &r) { name = new char[strlen(r.name)+1]; strcpy(name, r.name); } virtual Resume* Clone() { return new ResumeB(*this); } virtual void Show() { cout<<"ResumeB:"<<name<<endl; } }; #endif //Client #include "Resume.h" #include "ResumeA.h" #include "ResumeB.h" int main(int argc, char **argv) { Resume *r1 = new ResumeA("AAAAA"); Resume *r2 = new ResumeB("BBBBB"); Resume *r3 = r1->Clone(); Resume *r4 = r2->Clone(); delete r1; r1 = NULL; delete r2; r2 = NULL; r3->Show(); r4->Show(); system("pause"); return 0; }
带Prototype Manager的原型模式:
当系统中的原型数目不固定时(也就是说,它们可以动态创建和销毁),要保持一个可用原型的注册表。客户不会自己来管理原型,但会在注册表中存储和检索原型。
原型管理器(PrototypeManager)角色:创建具体原型类的对象,并记录每一个被创建的对象。
例子:
//Prototype Manager,有一个Add,一个Get #ifndef _RESUME_MANAGER_H #define _RESUME_MANAGER_H #include "Resume.h" #include <vector> using namespace std; class ResumeManager { private: vector<Resume*> mResume; public: ResumeManager(){} ~ResumeManager(){} void AddResume(Resume *resume) { mResume.push_back(resume); } Resume *GetIndex(int index) { if(index>=0 && index<mResume.size()) return mResume[index]; else return NULL; } }; #endif //Client #include "Resume.h" #include "ResumeA.h" #include "ResumeB.h" #include "ResumeManager.h" int main(int argc, char **argv) { ResumeManager *rManager = new ResumeManager(); Resume *r1 = new ResumeA("AAAAA"); Resume *r2 = new ResumeB("BBBBB"); rManager->AddResume(r1); rManager->AddResume(r2); rManager->GetIndex(0)->Show(); rManager->GetIndex(1)->Show(); Resume *r3 = rManager->GetIndex(0)->Clone(); Resume *r4 = rManager->GetIndex(1)->Clone(); delete r1; r1 = NULL; delete r2; r2 = NULL; r3->Show(); r4->Show(); delete r3; delete r4; r3 = r4 = NULL; system("pause"); return 0; }
Prototype模式的主要缺陷是每个Prototype子类都必须实现Clone操作。这很困难。比如考虑的类已经存在时就难以新增Clone操作。当内部包括一些不支持拷贝或者右循环引用的对象时,实现Clone也可能会很困难。
PS:注意深拷贝和浅拷贝。