1. 程式人生 > >設計模式(4)-物件建立型模式-Prototype模式

設計模式(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()並不會對其進行拷貝,需自行提供深拷貝;