1. 程式人生 > >面向物件設計模式之---原型模式(Prototype Pattern)

面向物件設計模式之---原型模式(Prototype Pattern)

原型模式的定義是:

用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。

這個概念看上去很抽象,其實質就是我們在程式設計中經常用到的物件複製,然後我們不免又要提到一個老生常談的話題淺複製與深複製

先看一下原型模式的UML類圖吧:

這裡寫圖片描述

其中,Prototype是一個原型的抽象類或藉口,它裡面有一個共有方法,叫clone。ConcretePrototype1與ConcretePrototype2是兩個具體的例項,繼承或實現了Prototype。這就對應了定義中用原型例項指定建立物件的種類。Client是客戶端類,它與Prototype是關聯的關係,即在Client類的例項中,有Prototype的物件。客戶端可以通過呼叫Prototype的clone方法來對實現了Prototype的ConcretePrototype1或ConcretePrototype2的物件進行復制來建立新物件,這樣比new會有更高的執行效率。

至於它的應有場景,我就拿《大話設計模式》中的例子吧,書中舉了一個求職簡歷的例子,我們投公司一般都是一投好幾家,簡歷也要準備好幾份,有的還需要針對公司和職位量身定製部分內容,如果拿軟體來生成簡歷,就比較適合使用原型模式來設計。

現在來談談淺複製與深複製的問題,所謂淺複製,簡而言之,就是在物件中的非引用型別都會被逐位複製,引用型別複製的是引用,也就是說,在複製後的物件中,如果原有物件中含有引用型別的話,那麼複製的物件與原物件指向同一個引用型別,這裡的引用型別指的是陣列、其他物件例項等。淺複製只做到了原物件與複製物件的相對獨立;而深複製就是連引用型別也跟著複製了,原物件與複製物件完全獨立。

這個例子我們用java來實現,在Java中有一個藉口替我們實現好了Prototype,這個介面叫做cloneable。Sun官方的宣告如下

public interface Cloneable
此類實現了 Cloneable 介面,以指示 Object.clone() 方法可以合法地對該類例項進行按欄位複製。
如果在沒有實現 Cloneable 介面的例項上呼叫 Object 的 clone 方法,則會導致丟擲 CloneNotSupportedException 異常。
按照慣例,實現此介面的類應該使用公共方法重寫 Object.clone(它是受保護的)。請參閱 Object.clone(),以獲得有關重寫此方法的詳細資訊。
注意,此介面不 包含 clone 方法。因此,因為某個物件實現了此介面就克隆它是不可能的。即使 clone 方法是反射性呼叫的,也無法保證它將獲得成功。

注意紅字,也就是說,這個介面告訴計算機,這個類是可複製的。clone預設實現的是淺複製,下面就舉一個利用原型模式和cloneable介面實現深複製的例子。在簡歷中一般包含有工作經驗,我們就把它抽象出一個類。

程式碼例項如下:

import java.util.*;

//工作經驗類
class Experience implements Cloneable
{
  private String time;//工作時間
  private String where;//工作地點

  //設定工作經驗
  public void setExperience(String time , String where)
  {
    this.time = time;
    this.where = where;
  }

  //顯示工作經驗
  public String toString()
  {
   return "在"+ where + "工作過,時間為"+time+"\n"; 
  }

  //克隆方法,用於深複製
  protected Object clone()throws CloneNotSupportedException
  {
    return super.clone();
  }

}

//簡歷類
class Resume implements Cloneable
{
   private String name;//名字
   private int age;//年齡
   private Experience exp;//工作經驗

   //構造方法
   public Resume()
   {
     this.exp = new Experience();
   }

   //設定名字
   public void setName(String name)
   {
      this.name = name;
   }

   //設定年齡
   public void setAge(int age)
   {
     this.age = age;
   }

   //設定工作經驗
   public void setExp(String time , String where)
   {
      this.exp.setExperience(time,where);
   }

   //顯示簡歷資訊
   public String toString()
   {
     return "姓名:"+this.name+" 年齡:"+ this.age+" 工作經驗:"+exp.toString();
   }

   //克隆方法
  protected Object clone()throws CloneNotSupportedException
  {
    Resume obj = (Resume)super.clone();
    obj.name = this.name;
    obj.age = age;
    obj.exp = (Experience)this.exp.clone();
    return obj;
  }

}

public class Main
{
  public static void main(String[] args)
  {
    try
    {
      //第一份簡歷
      Resume r1 = new Resume();
      r1.setName("Martin");
      r1.setAge(23);
      r1.setExp("2016年","Tencent");

      //第二份簡歷
      Resume r2 = (Resume)r1.clone();
      r2.setExp("2017年","Baidu");

      System.out.println(r1);
      System.out.println(r2);
    }catch(CloneNotSupportedException e)
    {
      e.printStackTrace();
    }
  }
}

執行結果如下:
這裡寫圖片描述