我所理解的設計模式(C++實現)——原型模式(Prototype Pattern)

LCL_data發表於2013-04-06

解決的問題:

用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。這個其實和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模式是克隆一個原型而不是請求工廠方法建立一個,所以它不需要一個與具體產品類平行的Creater類層次。

4.Portotype模式具有給一個應用軟體動態載入新功能的能力。由於Prototype的獨立性較高,可以很容易動態載入新功能而不影響老系統。

5.產品類不需要非得有任何事先確定的等級結構,因為Prototype模式適用於任何的等級結構

6.Prototype模式的最主要缺點就是每一個類必須配備一個克隆方法。而且這個克隆方法需要對類的功能進行通盤考慮,這對全新的類來說不是很難,但對已有的類進行改造時,不一定是件容易的事。


適用性:

1. 當一個系統應該獨立於他的產品建立、構成和表示時,需要使用原型模式

2. 當要例項化的類是在執行時刻指定時,如通過動態裝載

3. 為了避免建立一個與產品類層次平行的工廠類層次時

4. 當一個類的例項只能有幾個不同狀態組合中的一種時,建立相應數目的原型並克隆他們可能比每次用合適的狀態手工例項化該類更方便一些。



相關文章