1. 程式人生 > >原創不如山寨——原型模式詳解

原創不如山寨——原型模式詳解

原型對象 相同 繼承 技術 同時 stat 指向 stc 很多

1. 前言

現實世界中山寨這種行為往往意味著假冒偽劣,備受批判。但是在軟件開發中,山寨卻又不少可取之處。首先其“成分”和“質量”和原創不相上下;其次相比原創一個東西的時間開銷,山寨一個出來總歸是省時省力的,畢竟對於計算機,克隆一個對象要比創建一個對象性能好得多(拷貝對象不會執行構造方法)。如果在開發中我們需要一個類的多個實例,這些實例只在某些屬性細節上不同,相比直接new出它們的時間開銷,從一個實例原型拷貝出其他的實例或許是更可取的方法。而原型模式,或者說克隆模式就能夠幫我們做到這點。

2. 原型模式詳解

2.1 原型模式詳解

用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。

2.2 原型模式類結構

技術分享圖片

原型模式中的主要角色是Prototype類,含有clone方法供客戶端調用,從而創建它的拷貝對象。類結構簡單清晰。

2.3 原型模式的代碼實現

  • 首先創建原型類,其實現了Cloneable接口,並重寫自Object繼承的clone方法。
public class Prototype implements Cloneable {

    @Override
    protected Prototype clone() throws CloneNotSupportedException {
        return(Prototype)super.clone();
    }
}
  • 測試
public class TestCase {

    public static void main(String[] args) throws CloneNotSupportedException {

        Prototype pObj = new Prototype();

        Prototype cObj = pObj.clone();

        System.out.println(pObj);

        System.out.println(cObj);

    }
}

首先創建了一個原型類的實例,其次調用該實例的clone方法獲得了該實例的拷貝,分別打印出兩個對象的哈希值

  • 結果
    技術分享圖片

3. 關於對象拷貝的深拷貝和淺拷貝問題

3.1 什麽是深拷貝和淺拷貝

  • 淺拷貝
    上面我們通過實現標記接口Clone使得該對象具有創建一個和自己一摸一樣的拷貝對象的能力,然而這種拷貝是淺拷貝,與之相對的是深拷貝。關於淺拷貝和深拷貝,可以見下圖
    技術分享圖片

當原對象內部有一個非基本類型的引用變量時,淺拷貝意味著拷貝對象內部該變量和原對象內部的引用變量指向同一個對象,通俗的說,它只對外部對象進行拷貝,對內部引用變量指向的對象不進行真實拷貝,只是指向該對象而已。

  • 深拷貝
    技術分享圖片

深拷貝,除了拷貝外部對象外,其引用變量指向的對象也要拷貝,同時如果該對象還有引用變量,其指向的對象同樣需要拷貝,一直遞歸進行指導拷貝完成。可以看出,深拷貝是深度的拷貝。如果對象間的組合關系十分復雜的話,深度拷貝過程就類似於樹的遍歷了。

3.2 Java的Clone接口實現的是淺拷貝

對此,我們可以驗證如下,首先修改原型類,添加一個引用變量obj指向Object對象。

public class Prototype implements Cloneable {

    public Object obj=new Object();

    @Override
    protected Prototype clone() throws CloneNotSupportedException {
        return(Prototype)super.clone();
    }
}

測試比較原型對象和拷貝對象的obj變量是否指向同一Object對象

public class TestCase {

    public static void main(String[] args) throws CloneNotSupportedException {

        Prototype pObj = new Prototype();

        Prototype cObj = pObj.clone();

        System.out.println(pObj.obj.equals(cObj.obj));

    }
}

運行結果如下
技術分享圖片

3.3 使用序列化和反序列化實現深拷貝

實現深拷貝的方式很多,比如遞歸實現淺拷貝,或者工作中可以使用開源類庫來做。下面介紹一種使用對象序列化和反序列化實現深拷貝的方式。原型類必須實現另一個標記接口Serializable,且其內部引用變量指向的對象也必須實現該序列化接口,由於Object沒有實現該接口,所以會報出NotSerializableException異常。下面給出關鍵的序列化和反序列化代碼,就不修改原型類進行測試了。

import java.io.*;

public class TestCase {

    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {

        Prototype pObj = new Prototype();

        //序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(pObj);

        byte[] bytes = bos.toByteArray();

        //反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bis);
        Prototype cObj = (Prototype) ois.readObject();
        System.out.println(pObj.obj.equals(cObj.obj));

    }
}

總結

原型模式通過對象拷貝的方式獲得類的多個實例。在很多情況下這樣能獲得更好的性能。原型模式通常和其他模式結合使用,比如Spring框架中通過和工廠模式結合使得我們可以獲得相同Bean的多個實例,這樣幫我們我們屏蔽了創建新實例的復雜細節。

原創不如山寨——原型模式詳解