1. 程式人生 > 實用技巧 >設計模式——原型模式

設計模式——原型模式

原型模式(Prototype Pattern):是用於建立重複物件,同時又能保證效能。這種型別的設計模式屬於建立型模式,它提供了一種建立物件的最佳方式。這種模式是實現了一個原型介面,該介面用於建立當前物件的克隆。當直接建立物件的代價比較大時,則採用這種模式。

思想:Java 中 Object 類是所有類的根類,Object 類提供了一個 clone() 方法,該方法可以將一個 Java 物件複製一份,但是需要實現 clone 的 Java 類必須要實現一個介面 Cloneable,該介面表示該類能夠複製且具有複製的能力(原型模式)。

一、基本介紹


● 原型模式:用原型例項指定建立物件的種類,並且通過拷貝這些原型,建立新的物件。
● 原型模式是一種建立型設計模式,允許一個物件再建立另外一個可定製的物件,無需知道如何建立的細節。
● 工作原理是:通過將一個原型物件傳給那個要發動建立的物件,這個要發動建立的物件通過請求原型對像拷貝它們自己來實現建立,及物件的clone()。

二、類圖


原理結構圖說明 :1)、Prototype:原型類,宣告一個克隆自己的介面 clone。
2)、ConcretePrototype:具體的原型類,實現一個克隆自己的操作。
3)、Client 讓一個原型物件克隆自己,從而建立一個新的物件(相當於屬性)。

三、原型模式案例分析


【1】克隆類需要實現 Cloneable 重寫 clone 方法。

 1 package com.yintong.principle.singleresponsibility;
 2 //寫一個手機的克隆類
 3 public class ConcretePrototype implements Cloneable{
4 //名稱 5 private String name; 6 //號碼 7 private Long number; 8 //構造器 9 public ConcretePrototype(String name, Long number) { 10 super(); 11 this.name = name; 12 this.number = number; 13 } 14 15 @Override 16 public String toString() { 17 return
"ConcretePrototype [name=" + name + ", number=" + number + "]"; 18 } 19 // 克隆用到的主要部分 20 @Override 21 protected Object clone() throws CloneNotSupportedException { 22 ConcretePrototype ConcretePrototype = null; 23 try { 24 ConcretePrototype = (ConcretePrototype) super.clone(); 25 }catch (Exception e) { 26 System.out.println(e.getMessage()); 27 } 28 return ConcretePrototype; 29 } 30 }

【2】客戶端呼叫 clone 方法,實現原型模式。

1 public class Client {
2     public static void main(String[] args) throws CloneNotSupportedException {
3         //建立一個物件
4         ConcretePrototype prototype = new ConcretePrototype("華為", new Long(1568889932));
5         //通過原型模式完成物件的建立  克隆
6         ConcretePrototype p2 = (ConcretePrototype)prototype.clone();
7     }
8 }

四、Spring 中的應用


【1】當 scope 配置為 prototype 時,表示原型模式。

1  <!-- 這裡我們的 scope="prototype" 即 原型模式來建立 -->
2  <bean id="id01" class="com.atguigu.spring.bean.Monster" scope="prototype"/>

【2】檢視底層呼叫:

 1 else if (mbd.isPrototype()) {
 2     // It's a prototype -> create a new instance.
 3     Object prototypeInstance = null;
 4     try {
 5         beforePrototypeCreation(beanName);
 6             prototypeInstance = createBean(beanName, mbd, args);
 7     }
 8     finally {
 9         afterPrototypeCreation(beanName);
10     }
11         // ***  建立一個代理類  ***
12     bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
13 }

五、淺拷貝


● 對於資料型別是基本資料型別的成員變數,淺拷貝會直接進行值傳遞,也就是將該屬性值複製一份給新物件。
● 對於資料型別是引用型別的成員變數,比如說成員變數是某個陣列、某個類的物件等,那麼淺拷貝會進行引用傳值,也就是隻是將成員變數的引用值(記憶體地址)複製一份給新物件。因為實際上兩個物件的該成員變數都指向同一個例項。在這種情況下,在一個物件中修改該成員變數會影響到另一個物件的該成員變數值。
● 淺拷貝是使用預設的 clone() 方法來實現。

六、深拷貝


● 複製物件的所有基本資料型別的成員變數值
● 為所有引用資料型別的成員變數申請儲存空間,並複製每個引用資料型別成員變數所引用的物件,直到該物件可達的所有物件。也就是說,物件進行深拷貝要對整個物件進行拷貝。
● 深拷貝的實現方式有兩種,第一種是重寫 clone 方法來實現深拷貝,第二種是通過序列化實現深拷貝,也是推薦的一種。

七、深克隆的兩種實現方法


【1】通過呼叫引用型別的克隆方法,實現深拷貝。缺點就是當引用型別多時,不建議採用。

 1 public class DeepClone implements Cloneable{
 2     //基本資料型別
 3     private String name;
 4     //引用資料型別
 5     private Spare spare;
 6     
 7     //重寫clone方法,呼叫引用型別的克隆方法
 8     @Override
 9     protected Object clone() throws CloneNotSupportedException {
10         DeepClone deepClone = null;
11         deepClone = (DeepClone)super.clone();
12         
13         //克隆引用資料型別
14         deepClone.spare = (Spare) spare.clone();
15         return deepClone;
16     }
17 }

【2】通過序列化的方式,實現深拷貝:也是建議使用的方法。

 1 public class DeepClone implements Serializable{
 2     /**
 3      * 序列化 ID
 4      */
 5     private static final long serialVersionUID = 1L;
 6     //資料型別  略。。。。
 7     //重寫clone方法,呼叫引用型別的克隆方法
 8     protected Object deepClone(){
 9         ByteArrayOutputStream BOStream = null;
10         ObjectOutputStream OOSream = null;
11         ByteArrayInputStream BIStream = null;
12         ObjectInputStream OIStream =null;
13         try {
14             //序列化
15             BOStream = new ByteArrayOutputStream();
16             OOSream = new ObjectOutputStream(BOStream);
17             //將當前物件寫入流中
18             OOSream.writeObject(this);
19             
20             //反序列化
21             BIStream = new ByteArrayInputStream(BOStream.toByteArray());
22             OIStream = new ObjectInputStream(BIStream);
23             DeepClone deepClone = (DeepClone) OIStream.readObject();
24             return deepClone;
25         } catch (Exception e) {
26             e.printStackTrace();
27             return null;
28         }finally {
29             try {
30                 BOStream.close();
31                 OOSream.close();
32                 BIStream.close();
33                 OIStream.close();
34             } catch (Exception e2) {
35                 e2.printStackTrace();
36             }
37         }
38     }
39 }

八、原型模式的注意事項和細節


1)、建立新的物件比較複雜時,可以利用原型模式簡化物件的建立過程,同時也能夠提高效率。
2)、不用重新初始化物件,而是動態地獲得物件執行時的狀態。
3)、如果原始物件發生變化(增加或者減少屬性),其他克隆物件也會發生變化,無需修改程式碼。
4)、在實現深克隆的時候可能需要比較複雜的程式碼。
5)、缺點:需要為每一個配置類配置一個克隆方法,這對全新的類來說不是很難,但對已有的類進行改造時,需要修改其原始碼,違背了 ocp 原則。