設計模式(4)-物件建立型模式-Prototype模式
1.物件建立型模式
1.4 Protoype模式
1.4.1需求
通過拷貝原形物件建立新的物件。
1.4.2結構
•P r o t o t y p e(Gr a p h i c)
— 宣告一個克隆自身的介面。
•C o n c r e t e P r o t o t y p e(S t a ff、W h o l e N o t e、H a l fN o t e)
— 實現一個克隆自身的操作。
• C l i e n t(G r a p h i c To o l)
— 讓一個原型克隆自身從而建立一個新的物件。
1.4.3例子-C++
//Prototype.h
#ifndef _PROTOTYPE_H_
#define _PROTOTYPE_H_
class Prototype
{
public:
virtual ~Prototype();
virtual Prototype*Clone()const = 0;
protected:
Prototype();
private:
};
class ConcretePrototype:public Prototype
{
public:
ConcretePrototype();
ConcretePrototype(const ConcretePrototype& cp);
~ConcretePrototype();
Prototype* Clone() const;
protected:
private:
};
#endif //~_PROTOTYPE_H_
程式碼片斷2:Prototype.cpp
//Prototype.cpp
#include"Prototype.h"
#include<iostream>
using namespace std;
Prototype::Prototype()
{
}
Prototype::~Prototype()
{
}
Prototype* Prototype::Clone() const
{
return 0;
}
ConcretePrototype::ConcretePrototype()
{
}
ConcretePrototype::~ConcretePrototype()
{
}
ConcretePrototype::ConcretePrototype(const ConcretePrototype& cp)
{
cout<<"ConcretePrototype copy..."<<endl;
}
Prototype* ConcretePrototype::Clone() const
{
return newConcretePrototype(*this);
}
//main.cpp
#include"Prototype.h"
#include<iostream>
using namespace std;
int main(int argc,char*argv[])
{
Prototype* p= newConcretePrototype();
Prototype* p1=p->Clone();
return 0;
}
注:這裡只是說明概念,沒有涉及C++常見的深度拷貝問題.
1.4.4 例子-JAVA
在Java中,原型模式可以很簡單地實現,只要實現Cloneable這個標識性的介面,再覆蓋該介面中的clone()方法,即可“克隆”該實現類的任何一個物件。
class ConcretePrototype02 implements Cloneable{
private String name;
private ArrayList<String> nameList = new ArrayList<String>();
public ConcretePrototype02(String name) {
this.name = name;
this.nameList.add(this.name);
}
//新增nameList中的物件
public void setName(String name) {
this.nameList.add(name);
}
public ArrayList<String> getNameList() {
return this.nameList;
}
//覆蓋Object基類中的clone()方法,並擴大該方法的訪問許可權,具體化返回本型別
public ConcretePrototype02 clone() {
ConcretePrototype02self = null;
try {
self= (ConcretePrototype02) super.clone();
//以下這句是實現深拷貝的關鍵
// self.nameList =(ArrayList<String>) this.nameList.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return self;
}
}
//測試類
public class Client {
public static void main(String[] args) {
ConcretePrototype02prototype02 = new ConcretePrototype02("螞蟻 ...");
System.out.println(prototype02.getNameList());
//通過clone獲得一個拷貝
ConcretePrototype02fromClone02 = prototype02.clone();
fromClone02.setName("小螞蟻 ...");
System.out.println(fromClone02.getNameList());
System.out.println(prototype02.getNameList());
}
}
測試結果:
拷貝之前的原型: [螞蟻 ...] 拷貝得到的物件: [螞蟻 ..., 小螞蟻 ...] 拷貝之後的原型: [螞蟻 ..., 小螞蟻 ...] |
發現拷貝之後原來的物件持有的ArrayList<String>型別的nameList引用會隨著拷貝得到的fromClone物件執行了setName()方法而改變,這不是我們想要的結果,因為這意味著原型以及拷貝得到的物件共享同一個引用變數,這是執行緒不安全的。當我們去掉 上面clone()方法中被註釋的語句之後再測試,得到結果如下:
拷貝之前的原型: [螞蟻 ...] 拷貝得到的物件: [螞蟻 ..., 小螞蟻 ...] 拷貝之後的原型: [螞蟻 ...] |
在Java中使用原型模式Prototype是相當簡單的,只要記住幾點注意點,就可以方便地實現該模式了。由於使用clone()方法來拷貝一個物件是從記憶體二進位制流中進行IO讀寫,所以拷貝得到一個物件是不會執行該物件所對應類的建構函式的。總結如下:
1、建構函式不會被執行;
2、類的成員變數中若有引用型別的變數(陣列也是一種物件),預設的clone()並不會對其進行拷貝,需自行提供深拷貝;