軟件設計模式學習(八)原型模式

原型模式

在軟件系統中,有時候須要屢次建立某一類型對象,爲了簡化建立過程,能夠只建立一個對象,而後再經過克隆的方法複製出多個相同的對象,這就是原型模式的設計思想。java


模式定義

原型模式是一種對象建立模式,用原型實例指定建立對象的種類,而且經過複製這些原型建立新的對象。ide


模式結構

在這裏插入圖片描述

  1. Prototype(抽象原型類)測試

    抽象原型類是定義具備克隆本身方法的接口,是全部具體原型類的公共父類,能夠是抽象類也能夠是接口。this

  2. ConcretePrototype(具體原型類)prototype

    具體原型類實現具體克隆方法,在克隆方法中返回本身的一個克隆對象設計

  3. Client(客戶類)code

    讓一個原型克隆自身,從而建立一個新的對象,在客戶類中只需直接實例化或經過工廠方法等方式建立一個對象,再經過調用該對象的克隆方法複製多個相同的對象。對象


Java對原型模式的支持

Java語言中的原型模式實現很簡單,原型模式結構中定義了一個抽象原型類,全部的Java類都繼承自java.lang.Object,而Object類提供一個clone方法,能夠將一個Java對象複製一份。所以在Java中能夠直接使用Object提供的clone()方法來實現對象的克隆。blog

須要注意的是能實現克隆的Java類必須實現一個標識接口Cloneable,表示這個Java類支持複製。若是一個類沒有實現這個接口但調用了clone()方法,Java編譯器將拋出一個CloneNotSupportedException異常。繼承

public class PrototypeDemo implements Cloneable {

    @Override
    protected Object clone() {

        Object o = null;

        try {
            o = super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println("Not support cloneable");
        }
        return o;
    }
}
public class Client {

    PrototypeDemo prototypeDemo = new PrototypeDemo();
    PrototypeDemo prototypeDemo2 = (PrototypeDemo) prototypeDemo.clone();
}


深克隆與淺克隆

一般狀況下,一個類包含一些成員對象,在使用原型模式克隆對象時,根據其成員對象是否也克隆,原型模式可分爲兩種形式:深克隆和淺克隆。

  1. 淺克隆

    被複制對象的全部普通成員變量都具備與原來的對象相同的值,而全部對其餘對象的引用仍然指向原來對象。
    在這裏插入圖片描述

    obj1是原型對象,obj2爲複製後對象,containedObj1和containedObj2爲成員對象。

  2. 深克隆

    引用其餘對象的變量將指向被複制過的新對象,而不是原有被引用的對象。
    在這裏插入圖片描述

Java語言原型模式的實現

Java語言提供的clone()方法將對象複製一份並返回給調用者,通常而言,clone()方法知足:

  1. 對任何對象x,都有clone() != x,即克隆對象與原對象不是同一個對象。
  2. 對任何對象x,都有x.clone().getClass() == x.getClass(),即克隆對象與原對象的類型同樣。
  3. 若是對象的equals()方法定義恰當,那麼x.clone().equals(x)應該成立。

爲了獲取對象的一份拷貝,能夠利用Object類的clone()方法,具體步驟以下

  1. 在派生類中覆蓋基類的clone()方法,並聲明爲public。
  2. 在派生類的clone()方法中,調用super.clone()。
  3. 在派生類中實現Cloneable接口

經過覆蓋Object類的clone()方法實現淺克隆,若是須要實現深克隆,能夠經過序列化等方式來實現。


原型模式實例之郵件複製(淺克隆)

  1. 實例說明

    因爲郵件對象包含的內容較多(如發送者、標題、內容、日期、附件等),某系統現需提供一個郵件複製功能,對於已經建立好的郵件對象,能夠經過複製的方式建立一個新的郵件對象,若是須要改變某部份內容,無須修改原始的郵件對象,只須要修改複製後獲得的郵件對象便可。本例使用淺克隆實現郵件複製,即複製郵件(E-mail)的同時不復制附件。

  2. 實例代碼及解釋

    1. 抽象原型類Object

      Object做爲抽象原型類,提供了克隆方法clone(),用於建立一個原型對象,其clone()方法由JVM完成具體實現,用戶在使用時無須關心。

      public class Object {
      
          protected native Object clone() throws CloneNotSupportedException;
      }
    2. 具體原型類Email(郵件類)

      public class Email implements Cloneable {
      
          private Attachment attachment = null;
      
          public Email() {
      
          }
      
          public Email(Attachment attachment) {
              this.attachment = attachment;
          }
      
          public Attachment getAttachment() {
              return attachment;
          }
      
          @Override
          public Object clone() {
      
              Email clone = null;
      
              try {
                  clone = (Email) super.clone();
              } catch (CloneNotSupportedException e) {
                  e.printStackTrace();
              }
              return clone;
          }
      }
    3. 附件類Attachment

      public class Attachment {
      
          public void download() {
              System.out.println("下載附件");
          }
      }
    4. 客戶端測試類Client

      public class Client {
      
          public static void main(String[] args) {
      
              Email email = new Email(new Attachment());
      
              Email copyEmail = (Email) email.clone();
      
              System.out.println(email == copyEmail);
              System.out.println(email.getAttachment() == copyEmail.getAttachment());
          }
      }
    5. 結果分析

      編譯並運行客戶端測試類,輸出結果以下:
      在這裏插入圖片描述

      經過結果能夠看出,複製獲得的對象與原型對象的引用不一致,但兩個對象的成員對象是同一個,說明雖然對象自己複製了一份,但其成員對象在內存中沒有複製,原型對象與克隆對象都維持了對相同成員對象的引用。


原型模式實例之郵件複製(深克隆)

  1. 實例說明

    用深克隆實現郵件複製

  2. 實例代碼及解釋

    1. 具體原型類Email(郵件類)

      Email做爲具體類,因爲實現的是深克隆,無須使用Object的clone()方法;經過序列化的方法實現深克隆,因爲要將Email類型對象寫入流中,所以Email類需實現Serializable接口。

      public class Email implements Serializable {
      
          private Attachment attachment = null;
      
          public Email() {
      
              this.attachment = new Attachment();
          }
      
          public Attachment getAttachment() {
      
              return attachment;
          }
      
          public Object deepclone() throws IOException, ClassNotFoundException {
      
              //將對象寫入流中
              ByteArrayOutputStream bao = new ByteArrayOutputStream();
              ObjectOutputStream oos = new ObjectOutputStream(bao);
              oos.writeObject(this);
      
              //將對象從流中取出
              ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
              ObjectInputStream ois = new ObjectInputStream(bis);
              return (ois.readObject());
          }
      }
    2. 附件類Attachment

      Attachment類型對象也將被寫入流中,所以也需實現Serializable接口。

      public class Attachment implements Serializable {
      
          public void download() {
              System.out.println("下載附件");
          }
      }
    3. 客戶端測試類Client

      public class Client {
      
          public static void main(String[] args) {
      
              Email email, copyEmail = null;
      
              email = new Email();
      
              try {
                  copyEmail = (Email) email.deepclone();
              } catch (IOException e) {
                  e.printStackTrace();
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              }
      
              System.out.println(email == copyEmail);
              System.out.println(email.getAttachment() == copyEmail.getAttachment());
          }
      }
    4. 結果分析

      編譯並運行客戶端測試類,輸出結果以下:
      在這裏插入圖片描述

      經過結果能夠看出,複製獲得的對象與原型對象的引用不一致,原型對象與克隆對象對成員對象的引用不相同,說明其成員對象也複製了一份。

相關文章
相關標籤/搜索