原型模式

在設計模式中存在一種模式,可以通過一個原型對象克隆出多個一模一樣的對象,該模式稱之爲原型模式。

原型模式的定義如下:

原型模式(Prototype Pattern):使用原型實例指定創建對象的種類,並且通過克隆這些原型創建新的對象。原型模式是一種對象創建型模式。

在使用原型模式時,需要首先創建一個原型對象,再通過複製這個原型對象來創建更多同類型的對象。

原型模式的工作原理很簡單:將一個原型對象傳給哪個要發動創建的對象,這個要發動創建的對象通過請求原型對象克隆自己來實現創建過程。由於在軟件系統中經常會遇到需要創建多個相同或者類似對象的情況,因此原型模式在真實開發中的使用頻率還是非常高的。原型模式是一種“另類”的創建型模式,創建克隆對象的工廠就是原型類自身,工廠方法由克隆方法來實現。

需要注意的是,通過克隆方法所創建的對象是全新的對象,它們在內存中擁有新的地址;通常,對克隆所產生的對象進行的修改不會對原型對象造成任何影響,每一個克隆對象都是相互獨立的;通過不同的方式對克隆對象進行修改後,可以得到一系列相似但不完全相同的對象。

原型模式的UML圖如下:

原型模式主要包含一下3個角色:

1.Prototype(抽象原型類):它是聲明克隆方法的接口,是所有具體原型類的公共父類,可以是抽象類也可以是接口,甚至可以是具體實現類;

2.ConcretePrototype(具體原型類):它實現在抽象原型類中聲明的克隆方法,在克隆方法中返回自己的一個克隆對象;

3.Client(客戶類):讓一個原型對象克隆自身從而創建一個新的對象,在客戶類中只需要直接實例化或者通過工廠方法等方式創建一個原型對象,再通過調用該對象的克隆方法即可得到多個相同的對象。由於客戶類針對抽象原型類Prototype編程,因此用戶可以根據需要選擇具體原型類,系統具有較好的可擴展性,增加或更換具體原型類都很方便。

通過上面的介紹可以知道,原型模式的核心在於如何實現克隆方法,而克隆方法的實現可能不同的編程語言實現起來不太一樣,這裏以Java來說明。

Java實現克隆對象可以通過把要克隆的對象實現Cloneable接口,並重新clone方法,在clone方法中返回新創建的對象。但是這種方法實現的克隆屬於淺克隆,也就是如果被克隆的類的實例變量不僅有基本類型,還有引用類型,那麼淺克隆就無法將引用類型所指向的對象創建一個新對象,換句話說淺克隆只是複製了引用的地址,引用指向的對象還是原來的對象,如果修改了克隆後的對象,那麼原對象也會被修改。

有沒有方法解決這個問題呢?答案是肯定的,就是使用Java的對象的序列化和反序列化。

首先,需要被克隆的對象需要實現Serializable接口,然後在克隆方法中,先把對象序列化後寫入流中,然後再從流中讀取並反序列化成對象後返回。Java代碼實現如下:

import java.io.Serializable;


/**
 * 定義抽象的原型接口,實現序列號接口
 */
public interface Prototype extends Serializable {

    //聲明克隆方法(採用深克隆)
    Prototype deepClone() throws IOException, ClassNotFoundException;
}
import java.io.*;

/**
 * 具體原型類
 */
public class ConcretePrototype implements Prototype {


    /**
     * 通過序列化和反序列化實現深克隆
     * @return
     */
    @Override
    public Prototype deepClone() throws IOException, ClassNotFoundException {
        //將對象寫入流中
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(this);

        //將對象從流中讀出
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);

        return (ConcretePrototype)objectInputStream.readObject();
    }
}

通過這樣實現深克隆的方式,即使被克隆的對象的實例變量中存在引用類型,甚至引用類型指向的對象的實例變量還存在引用類型,都會創建一個全新的對象。這樣對克隆後對象的任意修改都不會影響到原對象。

原型模式的主要優點如下:

1.當創建新的對象實例較爲複雜時,使用原型模式可以簡化對象的創建過程,通過複製一個已有實例可以提高新實例的創建效率;

2.擴展性較好。由於再原型模式中提供了抽象的原型類,在客戶端可以針對抽象原型類進行編程,而將具體原型類寫在配置文件中,增加或減少具體原型類對原有系統都沒有任何影響;

3.原型模式提供了簡化的創建結構。工廠方法模式常常需要有一個與產品類等級結構相同的工廠等級結構,而原型模式就不需要這樣,原型模式中產品的複製是通過封裝在原型類中的克隆方法實現的,無須專門的工廠類創建產品;

4.可以使用深克隆的方式保存對象的狀態。使用原型模式將對象複製一份並將其狀態保存起來,以便在需要的時候使用,例如恢復到某一歷史狀態,可輔助實現撤銷操作。

原型模式的主要缺點如下:

1.需要爲每一個類配備一個克隆方法,而且該克隆方法位於一個類的內部,當對已有的類進行改造時,需要修改源代碼,違背了開閉原則;

2.在實現深克隆時需要編寫較爲複雜的代碼,而且當對象之間存在多重嵌套引用時,爲了實現深克隆,每一層對象對應的類都必須支持深克隆,實現起來可能比較麻煩。

原型模式的適用場景如下:

1.創建新對象的成本較大(例如初始化需要佔用較長的時間,佔用太多的CPU資源或網絡資源),新的對象可以通過原型模式對已有對象進行復制來獲得,如果是相似對象,則可以對成員變量稍作修改;

2.如果系統需要保存對象的狀態,而對象的狀態變化很小,或者對象本身佔用的內存較少時,可以使用原型模式配合備忘錄模式來實現;

3.需要避免使用分層次的工廠類來創建分層次的對象,並且類的實例對象只有一個或很少的幾個組合狀態,通過複製原型對象得到新實例可能比使用構造方法創建一個新實例更加方便。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章