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

1. 前言

現實世界中山寨這種行爲每每意味着假冒僞劣,備受批判。可是在軟件開發中,山寨卻又很多可取之處。首先其「成分」和「質量」和原創不相上下;其次相比原創一個東西的時間開銷,山寨一個出來總歸是省時省力的,畢竟對於計算機,克隆一個對象要比建立一個對象性能好得多(拷貝對象不會執行構造方法)。若是在開發中咱們須要一個類的多個實例,這些實例只在某些屬性細節上不一樣,相比直接new出它們的時間開銷,從一個實例原型拷貝出其餘的實例或許是更可取的方法。而原型模式,或者說克隆模式就可以幫咱們作到這點。框架

2. 原型模式詳解

2.1 原型模式定義

用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象。ide

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使得該對象具備建立一個和本身一摸同樣的拷貝對象的能力,然而這種拷貝是淺拷貝,與之相對的是深拷貝。關於淺拷貝和深拷貝,能夠見下圖

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

  • 深拷貝

深拷貝,除了拷貝外部對象外,其引用變量指向的對象也要拷貝,同時若是該對象還有引用變量,其指向的對象一樣須要拷貝,一直遞歸進行指導拷貝完成。能夠看出,深拷貝是深度的拷貝。若是對象間的組合關係十分複雜的話,深度拷貝過程就相似於樹的遍歷了。code

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對象blog

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));

    }
}

4. 總結

原型模式經過對象拷貝的方式得到類的多個實例。在不少狀況下這樣能得到更好的性能。原型模式一般和其餘模式結合使用,好比Spring框架中經過和工廠模式結合使得咱們能夠得到相同Bean的多個實例,這樣幫咱們咱們屏蔽了建立新實例的複雜細節。

相關文章
相關標籤/搜索