設計模式讀書筆記-----原型模式

以前聽過這樣一句話:“程序員的最高境界就是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、我們雖然可以利用原型模式來獲得一個新對象,但有時對象的複製可能會相當的複雜,比如深克隆。


PS:如果你覺得文章對你有所幫助,別忘了推薦或者分享,因爲有你的支持,纔是我續寫下篇的動力和源泉!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章