設計模式C++原型模式
問題描述
看到這個模式,很容易想到小時候看的《西遊記》,齊天大聖孫悟空發飆的時候可以通過自己頭上的 3 根毛立馬複製出來成千上萬的孫悟空, 對付小妖怪很管用(數量最重要)。
Prototype 模式也正是提供了自我複製的功能, 就是說新物件的建立可以通過已有物件進行建立。在 C++中,拷貝建構函式( Copy Constructor) 曾經是很對程式設計師的噩夢,淺層拷貝和深層拷貝的魔魘也是很多程式設計師在面試時候的快餐和系統崩潰時候的根源之一。
在GOF的《設計模式:可複用面向物件軟體的基礎》中是這樣說的:用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。這這個定義中,最重要的一個詞是“拷貝”,也就是口頭上的複製,而這個拷貝,也就是原型模式的精髓所在。
UML類圖
由於克隆需要一個原型,而上面的類圖中Prototype就這個原型,Prototype定義了克隆自身的Clone介面,由派生類進行實現,而實現原型模式的重點就在於這個Clone介面的實現。ConcretePrototype1類和ConcretePrototype2類繼承自Prototype類,並實現Clone介面,實現克隆自身的操作;同時,在ConcretePrototype1類和ConcretePrototype2類中需要重寫預設的複製建構函式,供Clone函式呼叫,Clone就是通過在內部呼叫重寫的複製建構函式實現的。在後續的編碼過程中,如果某個類需要實現Clone功能,就只需要繼承Prototype類,然後重寫自己的預設複製建構函式就好了。好比在C#中就提供了ICloneable介面,當某個類需要實現原型模式時,只需要實現這個介面的道理是一樣的。
使用場合
原型模式和建造者模式、工廠方法模式一樣,都屬於建立型模式的一種。簡單的來說,我們使用原型模式,就是為了建立物件。不過,適合原型模式的最好選擇如下:
1.當我們的物件型別不是開始就能確定的,而這個型別是在執行期確定的話,那麼我們通過這個型別的物件克隆出一個新的物件比較容易一些;
2.有的時候,我們需要一個物件在某個狀態下的副本,此時,我們使用原型模式是最好的選擇;例如:一個物件,經過一段處理之後,其內部的狀態發生了變化;這個時候,我們需要一個這個狀態的副本,如果直接new一個新的物件的話,但是它的狀態是不對的,此時,可以使用原型模式,將原來的物件拷貝一個出來,這個物件就和之前的物件是完全一致的了;
3.當我們處理一些比較簡單的物件時,並且物件之間的區別很小,可能就幾個屬性不同而已,那麼就可以使用原型模式來完成,省去了建立物件時的麻煩了;
4.有的時候,建立物件時,建構函式的引數很多,而自己又不完全的知道每個引數的意義,就可以使用原型模式來建立一個新的物件,不必去理會建立的過程。
->適當的時候考慮一下原型模式,能減少對應的工作量,減少程式的複雜度,提高效率。
程式碼實現
?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
#include <iostream>
#include <string>
using
namespace std;
class
Prototype
{
private :
string str;
public :
Prototype(string s)
{
str = s;
}
Prototype()
{
str =
"" ;
}
void
show()
{
cout << str << endl;
}
virtual
Prototype *clone() = 0;
};
class
ConcretePrototype1 : public
Prototype
{
public :
ConcretePrototype1(string s) :Prototype(s)
{}
ConcretePrototype1(){}
virtual
Prototype *clone()
{
ConcretePrototype1 *p =
new ConcretePrototype1();
*p = * this ;
return
p;
}
};
class
ConcretePrototype2 : public
Prototype
{
public :
ConcretePrototype2(string s) :Prototype(s)
{}
ConcretePrototype2(){}
virtual
Prototype *clone()
{
ConcretePrototype2 *p =
new ConcretePrototype2();
*p = * this ;
return
p;
}
};
int
main()
{
ConcretePrototype1 *test =
new ConcretePrototype1( "小李" );
ConcretePrototype2 *test2 = (ConcretePrototype2 *)test->clone();
test->show();
test2->show();
return
0;
}
|
執行結果:
?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
#include <iostream>
#include <string>
using
namespace std;
class
Resume
{
private :
string name,sex,age,timeArea,company;
public :
Resume(string s)
{
name=s;
}
void
setPersonalInfo(string s,string a)
{
sex=s;
age=a;
}
void
setWorkExperience(string t,string c)
{
timeArea=t;
company=c;
}
void
display()
{
cout<<name<< " " <<sex<< " " <<age<<endl;
cout<< "工作經歷: " <<timeArea<< " " <<company<<endl<<endl;
}
Resume *clone()
{
Resume *b;
b= new
Resume(name);
b->setPersonalInfo(sex,age);
b->setWorkExperience(timeArea,company);
return
b;
}
};
int
main()
{
Resume *r= new
Resume( "李俊巨集" );
r->setPersonalInfo( "男" , "26" );
r->setWorkExperience( "2007-2010" , "讀研究生" );
r->display();
Resume *r2=r->clone();
r2->setWorkExperience( "2003-2007" , "讀本科" );
r->display();
r2->display();
return
0;
}
|
執行結果: