解決的問題:
用原型執行個體指定建立對象的種類,並且通過拷貝這些原型建立新的對象。這個其實和C++的拷貝建構函式的作用是一致的,實際上就是動態抽取當前對象運行時的狀態。
類圖結構:
客戶(Client)角色:客戶類提出建立對象的請求。
抽象原型(Prototype)角色:這是一個抽象角色,通常由一個C#介面或抽象類別實現。此角色給出所有的具體原型類所需的介面。在C#中,抽象原型角色通常實現了ICloneable介面。
具體原型(Concrete Prototype)角色:被複製的對象。此角色需要實現抽象原型角色所要求的介面。
範例實現:
例子參照wuzhekai1985的簡曆的例子,代碼拷貝如下:
// CplusplusPrototype.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include<iostream>#include<vector>#include<assert.h>using namespace std;//父類 class Resume { protected: char *name; public: Resume() {} virtual ~Resume() {} virtual Resume* Clone() { return NULL; } virtual void Set(char *n) {} virtual void Show() {} }; class ResumeA : public Resume { public: ResumeA(const char *str); //建構函式 ResumeA(const ResumeA &r); //拷貝建構函式 ~ResumeA(); //解構函式 ResumeA* Clone(); //複製,關鍵所在 void Show(); //顯示內容 }; ResumeA::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::~ResumeA() { delete [] name;} ResumeA::ResumeA(const ResumeA &r) { name = new char[strlen(r.name)+1]; strcpy(name, r.name); } ResumeA* ResumeA::Clone() { return new ResumeA(*this); } void ResumeA::Show() { cout<<"ResumeA name : "<<name<<endl; } class ResumeB : public Resume { public: ResumeB(const char *str); //建構函式 ResumeB(const ResumeB &r); //拷貝建構函式 ~ResumeB(); //解構函式 ResumeB* Clone(); //複製,關鍵所在 void Show(); //顯示內容 }; ResumeB::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::~ResumeB() { delete [] name;} ResumeB::ResumeB(const ResumeB &r) { name = new char[strlen(r.name)+1]; strcpy(name, r.name); } ResumeB* ResumeB::Clone() { return new ResumeB(*this); } void ResumeB::Show() { cout<<"ResumeB name : "<<name<<endl; } int _tmain(int argc, _TCHAR* argv[]){Resume *r1 = new ResumeA("A"); Resume *r2 = new ResumeB("B"); Resume *r3 = r1->Clone(); Resume *r4 = r2->Clone(); r1->Show(); r2->Show(); //刪除r1,r2 delete r1; delete r2; r1 = r2 = NULL; //深拷貝所以對r3,r4無影響 r3->Show(); r4->Show(); delete r3; delete r4; r3 = r4 = NULL; return 0;}
帶Prototype Manager的原型模式:
客戶(Client)角色:用戶端類向原型管理器提出建立對象的請求。
抽象原型(Prototype)角色:這是一個抽象角色,通常由一個C#介面或抽象類別實現。此角色給出所有的具體原型類所需的介面。在C#中,抽象原型角色通常實現了ICloneable介面。
具體原型(Concrete Prototype)角色:被複製的對象。此角色需要實現抽象的原型角色所要求的介面。
原型管理器(Prototype Manager)角色:建立具體原型類的對象,並記錄每一個被建立的對象。
代碼實現如下:
// CplusplusPrototype.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include<iostream>#include<vector>#include<assert.h>using namespace std;//父類 class Resume { protected: char *name; public: Resume() {} virtual ~Resume() {} virtual Resume* Clone() { return NULL; } virtual void Set(char *n) {} virtual void Show() {} }; class ResumeA : public Resume { public: ResumeA(const char *str); //建構函式 ResumeA(const ResumeA &r); //拷貝建構函式 ~ResumeA(); //解構函式 ResumeA* Clone(); //複製,關鍵所在 void Show(); //顯示內容 }; ResumeA::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::~ResumeA() { delete [] name;} ResumeA::ResumeA(const ResumeA &r) { name = new char[strlen(r.name)+1]; strcpy(name, r.name); } ResumeA* ResumeA::Clone() { return new ResumeA(*this); } void ResumeA::Show() { cout<<"ResumeA name : "<<name<<endl; } class ResumeB : public Resume { public: ResumeB(const char *str); //建構函式 ResumeB(const ResumeB &r); //拷貝建構函式 ~ResumeB(); //解構函式 ResumeB* Clone(); //複製,關鍵所在 void Show(); //顯示內容 }; ResumeB::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::~ResumeB() { delete [] name;} ResumeB::ResumeB(const ResumeB &r) { name = new char[strlen(r.name)+1]; strcpy(name, r.name); } ResumeB* ResumeB::Clone() { return new ResumeB(*this); } void ResumeB::Show() { cout<<"ResumeB name : "<<name<<endl; } class ResumeManager{private:vector<Resume *> mResume;public:ResumeManager(){}void add(Resume * resume){mResume.push_back(resume);} Resume * get(int index) const{assert(index>=0 && index<mResume.size());return mResume[index];}};int _tmain(int argc, _TCHAR* argv[]){ResumeManager *manager = new ResumeManager();Resume *r1 = new ResumeA("A"); Resume *r2 = new ResumeB("B"); manager->add(r1);manager->add(r2); manager->get(0)->Show(); manager->get(1)->Show(); Resume *r3 = manager->get(0)->Clone(); Resume *r4 = manager->get(1)->Clone(); //刪除r1,r2 delete r1; delete r2; r1 = r2 = NULL; //深拷貝所以對r3,r4無影響 r3->Show(); r4->Show(); delete r3; delete r4; r3 = r4 = NULL; return 0;}
實現要點:
1.使用原型管理器,體現在一個系統中原型數目不固定時,可以動態建立和銷毀。
2.實現複製操作,在.NET中可以使用Object類的MemberwiseClone()方法來實現對象的淺表拷貝或通過序列化的方式來實現深拷貝,在C++中就是拷貝建構函式的作用。
3.Prototype模式同樣用於隔離類對象的使用者和具體類型(易變類)之間的耦合關係,它同樣要求這些“易變類”擁有穩定的介面。
效果:
1.它對客戶隱藏了具體的產品類,因此減少了客戶知道的名字的數目。
2. Prototype模式允許客戶只通過註冊原型執行個體就可以將一個具體產品類併入到系統中,客戶可以在運行時刻建立和刪除原型。
3.減少了子類構造,Prototype模式是複製一個原型而不是請求Factory 方法建立一個,所以它不需要一個與具體產品類平行的Creater類層次。
4.Portotype模式具有給一個應用軟體動態載入新功能的能力。由於Prototype的獨立性較高,可以很容易動態載入新功能而不影響老系統。
5.產品類不需要非得有任何事先確定的等級結構,因為Prototype模式適用於任何的等級結構
6.Prototype模式的最主要缺點就是每一個類必須配備一個複製方法。而且這個複製方法需要對類的功能進行通盤考慮,這對全新的類來說不是很難,但對已有的類進行改造時,不一定是件容易的事。
適用性:
1. 當一個系統應該獨立於他的產品建立、構成和表示時,需要使用原型模式
2. 當要執行個體化的類是在運行時刻指定時,如通過動態裝載
3. 為了避免建立一個與產品類層次平行的工廠類層次時
4. 當一個類的執行個體只能有幾個不同狀態組合中的一種時,建立相應數目的原型並複製他們可能比每次用合適的狀態手工執行個體化該類更方便一些。
LCL_data原創於CSDN.NET【http://blog.csdn.net/lcl_data/article/details/8764228】