我學設計模式 之 原型模型模式

sz_bdqn發表於2010-09-24

我學設計模式之原型模式

 

1.  簡介

原型模式屬於物件的建立模式。通過給出一個原型物件來指明所要建立的物件的型別,然後用複製這個原型物件的建立辦法建立出更多同型別的物件。

不通過new來建立物件而是通過拷貝來建立物件的模式就叫做原型模式。

2.  原型模式的結構

原型模式有兩種表現形式:第一種是簡單形式,第二種是登記形式。這兩種表現形式僅僅是原始模型模式的不同實現。

 

       2.1簡單形式的原始模型模式的類圖如下:

      

       這種形式設計3個角色:

       客戶角色:客戶類提出建立物件的請求。

       抽象原型:這是一個抽象角色,通常使用介面或抽象類實現。此角色給出所有具體原型類所需的介面。

       具體原型:被複制的物件。此角色需要實現抽象角色所提供的方法。

 

       下面用Java給出實現的原始碼:

       抽象原型程式碼:

package com.zsw.prototype;

public interface Prototype extends Cloneable {

    public Prototype clone();

 

    public void setName(String name);

    public String getName();

}

 

 

       具體原型程式碼:

package com.zsw.prototype;

 

public class ConcretePrototype implements Prototype {

    private String name;

    public String getName() {

       return name;

    }

    public void setName(String name) {

       this.name = name;

    }

    public Prototype clone(){

       try {

           return (Prototype) super.clone();

       } catch (CloneNotSupportedException e) {

           e.printStackTrace();

           return null;

       }

    }

}

       客戶端原型程式碼:

package com.zsw.prototype;

public class Client {

    public static void main(String[] args) {

       Prototype prototype = new ConcretePrototype();

       prototype.setName("shangwu");

       Prototype p = prototype.clone();

       System.out.println(prototype == p);

       System.out.println(prototype.getName());

       System.out.println(p.getName());

    }

}

 

2.2登記形式的原始模型模式:

此種形式原始模型模式的類圖如下:

作為原始模型模式的第二種形式,它有如下角色:

客戶端角色:客戶端向管理員提出建立物件的請求。

抽象原型角色:這是一個抽象角色,通常由一個介面或抽象類實現。此角色給出所有具體原型類所需的介面。

具體原型角色:被複制的物件,需要實現抽象原型角色定義的方法。

原型管理器角色:建立具體原型類的物件,並記錄每一個被建立的物件。

 

3.  原型模式的實現:深複製和淺賦值

複製或克隆有兩種方式。這兩種方式分別叫做淺複製(淺克隆)和深複製(深克隆)。

被複制物件的所有變數都含有與原來的物件相同的值,而所有對其他物件的引用仍然指向原來的物件。

淺複製僅僅複製所考慮物件,而不復制他所引用的物件。

 

深複製要複製的物件所引用的物件都複製一遍,而這種對被引用到的物件的複製叫做間接複製。

 

3.1 複製簡歷來實現淺複製

給出Java實現程式碼如下:

抽象原型角色:

此角色為Java中的Cloneable介面

具體原型型別:

package com.zsw.prototype.resume;

public class Resume implements Cloneable {

    private String name;

    private String sex;

    private int age;

    private WorkExperience work;

   

    public Resume(String name){

       this.name = name;

       work = new WorkExperience();

    }

   

    //設定個人資訊

    public void setPersonalInfo(String sex,int age){

       this.sex = sex;

       this.age = age;

    }

   

    //設定工作經歷

    public void setWorkExperience(String workDate,String company){

       work.setWorkDate(workDate);

       work.setCompany(company);

    }

   

    public void display(){

       System.out.println("姓名:"+name+"性別:"+sex+"年齡:" + age);

       System.out.println("工作日期:"+work.getWorkDate()+"工作公司:"+work.getCompany());

    }

   

    public Object clone(){

       try {

           return super.clone();

       } catch (CloneNotSupportedException e) {

           e.printStackTrace();

           return null;

       }

    }

}

 

工作經歷程式碼:

package com.zsw.prototype.resume;

 

public class WorkExperience {

    private String workDate;

 

    private String company;

    public String getCompany() {

       return company;

    }

 

    public void setCompany(String company) {

       this.company = company;

    }

 

    public String getWorkDate() {

       return workDate;

    }

 

    public void setWorkDate(String workDate) {

       this.workDate = workDate;

    }

}

客戶端:

package com.zsw.prototype.resume;

 

public class Client {

 

    public static void main(String[] args) {

       Resume a = new Resume("大鳥");

       a.setPersonalInfo("",29);

       a.setWorkExperience("1998-2000", "XXXX-公司");

      

       Resume b = (Resume)a.clone();

       b.setWorkExperience("1998 - 2006", "YY企業");

      

       Resume c = (Resume)a.clone();

       c.setWorkExperience("1998-2003", "ZZ企業");

      

       a.display();

       b.display();

       c.display();

    }

}

輸出結果:

姓名:大鳥性別:男年齡:29

工作日期:1998-2003工作公司:ZZ企業

姓名:大鳥性別:男年齡:29

工作日期:1998-2003工作公司:ZZ

姓名:大鳥性別:男年齡:29

工作日期:1998-2003工作公司:ZZ企業

此處的工作日期,和工作公司都是一樣,abc三個物件都引用工作經歷物件,但同時都引用了最後一個物件,所以三次輸出的資料都相同。這就是淺複製的現象,只複製所考慮的物件,而不復制它的引用物件。使用深複製可以解決此問題。

 

3.2 對簡歷進行改造,使用深複製來實現

具體實現程式碼如下:

       工作經歷類:

package com.zsw.prototype.resume;

 

public class WorkExperience implements Cloneable {

    private String workDate;

 

    private String company;

    public String getCompany() {

       return company;

    }

 

    public void setCompany(String company) {

       this.company = company;

    }

 

    public String getWorkDate() {

       return workDate;

    }

 

    public void setWorkDate(String workDate) {

       this.workDate = workDate;

    }

   

    public Object clone(){

       try {

           return super.clone();

       } catch (CloneNotSupportedException e) {        

           e.printStackTrace();

           return null;

       }

    }

}

 

 

簡歷原始碼類:

package com.zsw.prototype.resume;

 

public class Resume implements Cloneable {

 

    private String name;

    private String sex;

    private int age;

    private WorkExperience work;

   

    public Resume(String name){

       this.name = name;

       work = new WorkExperience();

    }

   

    //設定個人資訊

    public void setPersonalInfo(String sex,int age){

       this.sex = sex;

       this.age = age;

    }

   

    //設定工作經歷

    public void setWorkExperience(String workDate,String company){

       work.setWorkDate(workDate);

       work.setCompany(company);

    }

   

    public void display(){

       System.out.println("姓名:"+name+"性別:"+sex+"年齡:" + age);

       System.out.println("工作日期:"+work.getWorkDate()+"工作公司:"+work.getCompany());

    }

   

    public Object clone(){

       try {

           Resume resume = (Resume) super.clone();

           resume.work = (WorkExperience) this.work.clone();

           return resume;

       } catch (CloneNotSupportedException e) {

           e.printStackTrace();

           return null;

       }

    }

}

 

 

執行結果如下:

姓名:大鳥性別:男年齡:29

工作日期:1998-2000工作公司:XXXX-公司

姓名:大鳥性別:男年齡:29

工作日期:1998 - 2006工作公司:YY企業

姓名:大鳥性別:男年齡:29

工作日期:1998-2003工作公司:ZZ企業

 

       這樣結果就是所要的結果了,看到這裡應該深複製與淺複製應該很清楚了吧。

 

4.  原型模式的應用場景

1.    孫大聖身外身法術

2.    .Net中資料集物件DataSet,就有Clone()方法和Copy()方法,Clone()方法用來複制DataSet的結構,但不復制DataSet的資料,實現了原型模式的淺複製。Cope方法不但複製了結構,也複製了資料。在這裡Clone()Copy()分別實現了原型模式中的淺複製和深複製。

3.    簡歷的複製

 

 

2.3對兩種形式的比較:

如果要建立的原型物件數目較少而且比較固定的話,可以採用第一種形式,即簡單形式的原始模型。

如果要建立的原型物件數目不固定的話,可以採取第二種形式,也即登記形式的原始模型模式。

5.  原型模式的有點和缺點

n         原型模式允許動態的新增或減少產品類。

n         原始模型模式提供簡單的建立結構。

n         原始模型模式具有給一個應用軟體動態載入新的功能能力。

n         產品類不需要非得有任何事先確定的等級結構。

 

原始模型模式的最主要的缺點是每一個類都必須配備一個克隆的方法。

 

 

6.  參考文件

n         Java與模式》

n         《大話設計模式》

 

 

 

待續………..

 

相關文章