設計模式之原型模式

語言: CN / TW / HK

「這是我參與11月更文挑戰的第27天,活動詳情檢視:2021最後一次更文挑戰

本篇文章是設計模式專題的第五篇文章,我會將遇到的設計模式都一一總結在該專題下,我會把自己對每一種設計模式的感悟寫下來,以及在實際工作中我們該如何去靈活應用這些設計模式,歡迎大家關注。本篇文章我們就來講一講,用於建立重複物件的原型模式。

原型模式的簡單介紹

原型模式也屬於建立型模式的一種,它是用來建立重複物件的,也就是我們常說的拷貝物件。

原型模式是通過實現一個原型介面,然後呼叫clone()就可以完成物件複製,需要注意的是clone()方法是Object提供的,當需要深拷貝的時候,就需要通過重寫克隆方法進行物件的拷貝。

原型模式類圖:

image.png

原型模式擴充套件:

原型模式可以進行擴充套件,在原有的基礎上增加一個原型管理器 PrototypeManager 類。該類用 HashMap 快取多個複製的原型,客戶端可以通過管理器的 get(String id) 方法從中獲取複製的原型。

image.png

淺拷貝與深拷貝:

  • 淺拷貝:不重寫clone方法的都是淺拷貝,淺拷貝如果物件內屬性是引用型別的話,拷貝的是引用物件的地址。
  • 深拷貝:深拷貝需要重寫clone方法,通過序列話或者其他方式將屬性是引用型別的建立空間拷貝一份獨立的。

原型模式的具體實現思路

  • 淺拷貝:直接實現Cloneable介面即可

  • 深拷貝:需要實現Cloneable介面,並且需要重寫clone方法。

  • 原型管理器:

    • 建立抽象原型物件,繼承Cloneable介面
    • 建立抽象原型物件的具體實現
    • 建立原型管理器,初始化快取,提供獲取拷貝物件的方法

原型模式的具體實現方案

  • 淺拷貝

    public class Prototype implements Cloneable {    @Override    public Object clone()  throws CloneNotSupportedException {        return super.clone();   } }

  • 深拷貝

    深拷貝的方式有很多,我們的目的是為了將引用型別重新開闢空間,而不是引用原引用的地址。

    // 實現的方式有很多,針對屬性進行再次拷貝賦值,或者使用序列化 // 1. 再次拷貝 public class Prototype implements Cloneable {    private OtherPrototype other;        @Override    public Object clone()  throws CloneNotSupportedException {        Object obj = super.clone(); // 將原引用的內容,重新開闢空間儲存一份,使用新的引用        obj.setOther((OtherPrototype) obj.getOther.clone());        return obj;   } } // 2. 序列化實現 注意需要實現Serializable public class Prototype implements Serializable {    public Object deepClone() throws Exception   {        // 序列化        ByteArrayOutputStream bos = new ByteArrayOutputStream();        ObjectOutputStream oos = new ObjectOutputStream(bos);        oos.writeObject(this);        // 反序列化        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());        ObjectInputStream ois = new ObjectInputStream(bis);        return ois.readObject();   } }

  • 原型管理器方式

    // 抽象原型物件 public interface Prototype extends Cloneable {    @Override    public Object clone(); } // 真實原型物件 public class Realizetype implements Prototype {    @Override    public Object clone()  throws CloneNotSupportedException {        return super.clone();   } } // 原型管理器 class ProtoTypeManager {    // 快取    private HashMap<String, Object> cache = new HashMap<String, Object>();        // 初始化快取    public ProtoTypeManager() {        cache.put("realizetype", new Realizetype());   }        // 向快取中新增原型物件    public void addProtoType(String key, Object obj) {        cache.put(key, obj);   }    // 獲取對應原型物件的拷貝物件    public Prototype getPrototype(String key) {        Object temp = cache.get(key);        return (Prototype) temp.clone();   } }

原型模式的優缺點

優點

  • 原型模式效能很好,基於二進位制的流複製比new效能更好。
  • 原型模式可以避免建構函式的約束。
  • Java 為我們將原型模式封裝好,我們使用很方便。

缺點

  • 當需要深拷貝時,一個類引用不支援序列化的間接物件,或者引用含有迴圈結構的時候,拷貝會變的很困難。
  • 必須實現 Cloneable 介面。
  • clone方法位於類的內部,如果需要對已有類進行改造,需要改變類的內部結構,違背了開閉原則。

原型模式的適用場景

  1. 物件之間相同或相似,只是個別的幾個屬性不同的時候。
  2. 建立物件成本較大,比如初始化時間長,佔用CPU資源多,佔用網路資源多等,需要優化資源。
  3. 建立一個物件需要繁瑣的資料準備或訪問許可權等,需要提高效能或者提高安全性。
  4. 系統中大量使用該類物件,需要各個呼叫者給它的屬性重新賦值。
  5. 當一個物件需要提供給其他物件訪問,並且各個呼叫者都需要修改其值時。
  6. 原型模式很少單獨出現,一般是伴隨工廠方法模式一起出現,通過 clone 的方法建立一個物件,然後由工廠方法提供給呼叫者。

原型模式總結

原型模式雖然實現起來比較簡單,用法也很簡單。但是深拷貝和淺拷貝的問題我們一定要搞清楚,有時候使用淺拷貝導致資料混亂,排查起來也是很困難。再就是我們要善用原型管理器,通過原型管理器建立克隆物件會使我們事半功倍。