在做面向對象的軟件開發時我們往往想達到更高的代碼可複用性和更合理的軟件顆粒度。
根據《設計模式——可複用面向對象軟件的基礎》所說:“你必須找到相關的對象,以適當的顆粒度將他們迴歸類,再定義類的接口和繼承層次,建立對象之間的基本關係。你的設計應該對手頭的問題有針對性,同時對將來的問題和需求也要有足夠的通用性。”
內行的設計者知道:不是解決任何問題都要從頭做起。他們更願意複用以前使用的解決方案。這些重複的模式方案解決特定的問題,使得面向對象的設計更靈活、優雅,最終複用性更好。它們幫助設計者將新的設計建立在以往的工作基礎上,複用以往的成功設計方案。一個熟悉這些設計模式的設計者不需要再去發現它們,而能夠立即將他們應用於設計問題中。
本系列文章主要參考文獻爲——設計模式,可複用面向對象軟件的基礎(Design Patterns Elements of Reusable Object-Oriented SoftWare Erich.),內部代碼基本用C++語言編寫。
文章目錄
摘要
本章主要說明原型模式,該設計模式主要意圖是:用原型實例指定創建對象,並且通過拷貝這些原型創建新的對象;
簡單地說就是克隆一個和自己相同的對象。
主要參與者
該設計模式的參與者有三個,分別是:
- Prototype 抽象原型,克隆自生的抽象接口;
- ConcretePrototype 具體原型實現,實現克隆自生各個部件的具體實現接口;
- Client 用戶,讓一個原型克隆自生從而創建一個新的對象;
優點
對於該設計模式的優點大概說以下幾點,如有不同請提出寶貴意見:
- 對客戶影藏具體的產品類,減少客戶知道的名字和數量;(影藏的是產生的中間子類,客戶只知道有Prototype 不知道 PrototypeA的存在)
- 方便在運行時刻增加和刪除產品;
- 改變值以指定新的對象;
- 改變結構以指定新的對象;
- 減少子類的構造;
- 用類動態配置應用;
類似設計模式
抽象工廠模式;(使用案例待後續補充)
一般聯合使用的設計模式
抽象工廠模式、組合模式、解釋器模式;(使用案例待後續補充)
實施流程與具體實現
總體流程
- 客戶實例化初步原型產品對象;
- 客戶克隆已經實例化產品對象;
具體實現代碼
接下來我們通過一個實例代碼來說明具體實現:
抽象原型類(Prototype)
//抽象原型類
class Prototype
{
private:
string m_CarModel;
string m_Engine;
string m_GearBox;
string m_Chassis;
public:
Prototype(){};
virtual ~Prototype(){}
virtual Prototype* Clone() { return NULL; }
virtual void Set(string str) {}
virtual void Show() {}
}
具體原型類(Concrete Prototype )
//具體原型類1
class PrototypeBMW520: public Prototype
{
public:
//構造函數
PrototypeBMW520(const string *str){
if(str.size() == 0)
{
m_CarModel = "CarModel " + "BMW520";
m_Engine = "Engine " + "BMW520";
m_GearBox = "GearBox " + "BMW520";
m_Chassis = "Chassis " + "BMW520";
}
else
{
m_CarModel = "CarModel " + "BMW520 " + str;
m_Engine = "Engine " + "BMW520 " + str;
m_GearBox = "GearBox " + "BMW520 " + str;
m_Chassis = "Chassis " + "BMW520 " + str;
}
}
//拷貝構造函數
PrototypeBMW520(const PrototypeBMW520 &r){
m_CarModel = r.m_CarModel;
m_Engine = r.m_Engine;
m_GearBox = r.m_GearBox;
m_Chassis = r.m_Chassis;
}
~PrototypeBMW520(){}
PrototypeBMW520* Clone(){ return NULL; }
void Set(string str) {
m_CarModel = "CarModel " + "BMW520 " + str;
m_Engine = "Engine " + "BMW520 " + str;
m_GearBox = "GearBox " + "BMW520 " + str;
m_Chassis = "Chassis " + "BMW520 " + str;
}
void Show() {
cout<< "PrototypeBMW520"<< endl;
cout<< m_CarModel << endl;
cout<< m_Engine << endl;
cout<< m_GearBox << endl;
cout<< m_Chassis << endl;
}
}
//具體原型類2
class PrototypeBMW320: public Prototype
{
public:
//構造函數
PrototypeBMW320(const string *str){
if(str.size() == 0)
{
m_CarModel = "CarModel " + "BMW320";
m_Engine = "Engine " + "BMW320";
m_GearBox = "GearBox " + "BMW320";
m_Chassis = "Chassis " + "BMW320";
}
else
{
m_CarModel = "CarModel " + "BMW320 " + str;
m_Engine = "Engine " + "BMW320 " + str;
m_GearBox = "GearBox " + "BMW320 " + str;
m_Chassis = "Chassis " + "BMW320 " + str;
}
}
//拷貝構造函數
PrototypeBMW320(const PrototypeBMW320 &r){
m_CarModel = r.m_CarModel;
m_Engine = r.m_Engine;
m_GearBox = r.m_GearBox;
m_Chassis = r.m_Chassis;
}
~PrototypeBMW320(){}
PrototypeBMW320* Clone(){
return new PrototypeBMW320( *this );
}
void Set(string str) {
m_CarModel = "CarModel " + "BMW320 " + str;
m_Engine = "Engine " + "BMW320 " + str;
m_GearBox = "GearBox " + "BMW320 " + str;
m_Chassis = "Chassis " + "BMW320 " + str;
}
void Show() {
cout<< "PrototypeBMW320"<< endl;
cout<< m_CarModel << endl;
cout<< m_Engine << endl;
cout<< m_GearBox << endl;
cout<< m_Chassis << endl;
}
}
客戶(Client)
main()
{
Prototype *p_car1 = new PrototypeBMW320("No.1");
Prototype *p_car2 = new PrototypeBMW520("No.2");
Prototype *p_car3 = p_car1->Clone();
Prototype *p_car4 = p_car2->Clone();
p_car1->Show();
p_car2->Show();
//刪除p_car1 ,p_car2
delete p_car1 ;
delete p_car2 ;
p_car1 = p_car2 = NULL;
//深拷貝所以對p_car3 ,p_car4無影響
p_car3->Show();
p_car4->Show();
delete p_car3;
delete p_car4;
p_car3 = p_car4 = NULL;
}
補充說明
本篇博客通過一個簡單的例子說明了原型模式的實現具體流程,正如開題所說的原型模式主要的作用是克隆拷貝現有對象;附加博主完成該博客的一些思考——
1、該模式可以快速克隆一個相同的對象,比如說某一個對象在建立時需要設置大量的內部屬性,而我們再建一個相同的對象時沒有克隆的方法是否需要再次設置所有的參數呢?
2、該模式可以更靈活的使用,比如說所產生的對象有一些相同點,但也有一些不同點,我們是否能夠對相同點採用原型模式克隆,而不同給定一些函數接口來設置呢?
本文中提到了深拷貝可淺拷貝的概念,對於這兩個簡單的說一下:
- 淺拷貝: 將原對象或原數組的引用直接賦給新對象,新數組,新對象/數組只是原對象的一個引用
- 深拷貝: 創建一個新的對象和數組,將原對象的各項屬性的“值”(數組的所有元素)拷貝過來,是“值”而不是“引用”
關於深拷貝、淺拷貝可參考C++面試題之淺拷貝和深拷貝的區別
對於文章中直接用 " = " 做string的賦值運算存有疑慮,所以寫了一小段程序來驗證是深拷貝還是淺拷貝,結果如下:
本篇博客中的代碼爲純手寫未做編譯,如有Bug,請提出寶貴意見~