1. 程式人生 > >23種設計模式-原型模式(3)

23種設計模式-原型模式(3)

原創作者: chenssy  出處: http://www.cnblogs.com/chenssy/  本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。

以前聽過這樣一句話:“程式設計師的最高境界就是Ctrl+C、Ctrl+V”,我們先不論這句話的對錯,就論這個過程,這個過程我們都知道無非就是複製一個物件,然後將其不斷地貼上。這樣的過程我們可以將其稱之為“克隆”。再如我們應聘的時候列印了那麼多的簡歷。

1111

       克隆我們都清楚,就是用一個物體複製若干個一模一樣物體。同樣,在面向物件系統中,我們同樣可以利用克隆技術來克隆出若干個一模一樣的物件。在應用程式中,有些物件比較複雜,其建立過程過於複雜,而且我們又需要頻繁的利用該物件,如果這個時候我們按照常規思維new該物件,那麼務必會帶來非常多的麻煩,這個時候我們就希望可以利用一個已有的物件來不斷對他進行復制就好了,這就是程式設計中的“克隆”。這裡原型模式就可以滿足我們的“克隆”,在原型模式中我們可以利用過一個原型物件來指明我們所要建立物件的型別,然後通過複製這個物件的方法來獲得與該物件一模一樣的物件例項。這就是原型模式的設計目的。

一、模式定義

一、模式定義

     通過前面的簡單介紹我們就可以基本確定原型模式的定義了。所謂原型模式就是用原型例項指定建立物件的種類,並且通過複製這些原型建立新的物件。

      在原型模式中,所發動建立的物件通過請求原型物件來拷貝原型物件自己來實現建立過程,當然所發動建立的物件需要知道原型物件的型別。這裡也就是說發動建立的物件只需要知道原型物件的型別就可以獲得更多的原型例項物件,至於這些原型物件時如何建立的根本不需要關心。

      講到原型模式了,我們就不得不區分兩個概念:深拷貝、淺拷貝。

      淺拷貝:使用一個已知例項對新建立例項的成員變數逐個賦值,這個方式被稱為淺拷貝。

     深拷貝:當一個類的拷貝構造方法,不僅要複製物件的所有非引用成員變數值,還要為引用型別的成員變數建立新的例項,並且初始化為形式引數例項值。

11111

      對於深拷貝和淺拷貝的詳細情況,請參考這裡:漸析java的淺拷貝和深拷貝

二、模式結構

二、模式結構

下圖是原型模式的UML結構圖:

2222

       原型模式主要包含如下三個角色:

       Prototype:抽象原型類。宣告克隆自身的介面。         ConcretePrototype:具體原型類。實現克隆的具體操作。         Client:客戶類。讓一個原型克隆自身,從而獲得一個新的物件。

      我們都知道Object是祖宗,所有的Java類都繼承至Object,而Object類提供了一個clone()方法,該方法可以將一個java物件複製一份,因此在java中可以直接使用clone()方法來複制一個物件。但是需要實現clone的Java類必須要實現一個介面:Cloneable.該介面表示該類能夠複製且具體複製的能力,如果不實現該介面而直接呼叫clone()方法會丟擲CloneNotSupportedException異常。如下:

public class PrototypeDemo implements Cloneable{
  public Object clone(){
    Object object = null;
    try {
      object = super.clone();
    } catch (CloneNotSupportedException exception) {
      System.err.println("Not support cloneable");
    }
    return object;
    }
    ……
}

      Java中任何實現了Cloneable介面的類都可以通過呼叫clone()方法來複制一份自身然後傳給呼叫者。一般而言,clone()方法滿足:        (1) 對任何的物件x,都有x.clone() !=x,即克隆物件與原物件不是同一個物件。        (2) 對任何的物件x,都有x.clone().getClass()==x.getClass(),即克隆物件與原物件的型別一樣。        (3) 如果物件x的equals()方法定義恰當,那麼x.clone().equals(x)應該成立。

三、模式實現

三、模式實現

影印簡歷各位都應該做過吧!這裡我們利用原型模式來模擬影印簡歷。

      簡歷:Resume.java

public class Resume implements Cloneable {
    private String name;
    private String birthday;
    private String sex;
    private String school;
    private String timeArea;
    private String company;
    
    /**
     * 建構函式:初始化簡歷賦值姓名
     */
    public Resume(String name){
        this.name = name;
    }
    
    /**
     * @desc 設定個人基本資訊
     * @param birthday 生日
     * @param sex 性別
     * @param school 畢業學校
     * @return void
     */
    public void setPersonInfo(String birthday,String sex,String school){
        this.birthday = birthday;
        this.sex = sex;
        this.school = school;
    }
    
    /**
     * @desc 設定工作經歷
     * @param timeArea 工作年限
     * @param company 所在公司
     * @return void
     */
    public void setWorkExperience(String timeArea,String company){
        this.timeArea = timeArea;
        this.company = company;
    }
    
    /**
     * 克隆該例項
     */
    public Object clone(){
        Resume resume = null;
        try {
            resume = (Resume) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return resume;
    }
    
    public void display(){
        System.out.println("姓名:" + name);
        System.out.println("生日:" + birthday + ",性別:" + sex + ",畢業學校:" + school);
        System.out.println("工作年限:" + timeArea + ",公司:" + company);
    }

}

      客戶端:Client.java

public class Client {
    public static void main(String[] args) {
        //原型A物件
        Resume a = new Resume("小李子");
        a.setPersonInfo("2.16", "男", "XX大學");
        a.setWorkExperience("2012.09.05", "XX科技有限公司");
        
        //克隆B物件
        Resume b = (Resume) a.clone();
        
        //輸出A和B物件
        System.out.println("----------------A--------------");
        a.display();
        System.out.println("----------------B--------------");
        b.display();
        
        /*
         * 測試A==B?
         * 對任何的物件x,都有x.clone() !=x,即克隆物件與原物件不是同一個物件
         */
        System.out.print("A==B?");
        System.out.println( a == b);
        
        /*
         * 對任何的物件x,都有x.clone().getClass()==x.getClass(),即克隆物件與原物件的型別一樣。
         */
        System.out.print("A.getClass()==B.getClass()?");
        System.out.println(a.getClass() == b.getClass());
    }
}

      執行結果:

44444 

四、模式優缺點

四、模式優缺點

      1、如果建立新的物件比較複雜時,可以利用原型模式簡化物件的建立過程,同時也能夠提高效率。

      2、可以使用深克隆保持物件的狀態。

      3、原型模式提供了簡化的建立結構。

缺點 

      1、在實現深克隆的時候可能需要比較複雜的程式碼。

      2、需要為每一個類配備一個克隆方法,而且這個克隆方法需要對類的功能進行通盤考慮,這對全新的類來說不是很難,但對已有的類進行改造時,不一定是件容易的事,必須修改其原始碼,違背了“開閉原則”。

五、模式使用場景

      1、如果建立新物件成本較大,我們可以利用已有的物件進行復制來獲得。

      2、如果系統要儲存物件的狀態,而物件的狀態變化很小,或者物件本身佔記憶體不大的時候,也可以使用原型模式配合備忘錄模式來應用。相反,如果物件的狀態變化很大,或者物件佔用的記憶體很大,那麼採用狀態模式會比原型模式更好。        3、需要避免使用分層次的工廠類來建立分層次的物件,並且類的例項物件只有一個或很少的幾個組合狀態,通過複製原型物件得到新例項可能比使用建構函式建立一個新例項更加方便。

六、模式總結

      1、原型模式向客戶隱藏了建立物件的複雜性。客戶只需要知道要建立物件的型別,然後通過請求就可以獲得和該物件一模一樣的新物件,無須知道具體的建立過程。

      2、克隆分為淺克隆和深克隆兩種。

      3、我們雖然可以利用原型模式來獲得一個新物件,但有時物件的複製可能會相當的複雜,比如深克隆。