跳到主要內容

原型模式 (Prototype Pattern)

        原型模式,光看字面上的意思很難了解這在幹麻,我們直接先來看正式定義及類別圖:

(中文好難翻譯,我不會…)
Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype.


        從定義上我們大概可以知道,這個模式的用途是讓我們可以透過複製的方式產生一個新物件。可是產生物件幹麻要這麼麻煩,不是直接 new 出一個新物件就好了嗎? 還是一句老話:有時建立物件的代價很高。假如你所需要建立的物件,其所需的資料是放在遠端資料庫的,這樣當每建立一個物件,就要讀取資料庫一次,這其實是很花成本的。

        從類別圖我們可以看到,有一個公開的介面 Prototype,其子類別都需要實作 clone。而 client 這邊在取得物件時,不是透過 new 的方式,而是使用 clone() 來取得所需的物件。因此我們可以知道,此模式的重點在於 client 的程式碼可以在不知道特定類別為何的情形下可以生成新的物件

        接下來來看簡單的範例程式碼吧。在 Java 中,每個類別都繼承了 Object 這個類別,表示每個類別都含有 clone() 這個方法。但是要讓 clone() 能動,類別還必須實作 java.lang.Cloneable 這個標籤介面(Tag Interface,Marker Interface)。除了這點之外,還要注意你的 clone() 是想要淺層複製 (shallow copy) 還是深層複製 (deep copy)。

// 在 Java, 要實作 Cloneable
// 才能 override Object 的 clone() 方法
public class SomeObj implements Cloneable {

    // 某個 primitive type 成員,
    // shallow copy 可以複製值
    private int mNum;

    // 某個類別物件,
    // shallow copy 只會複製 reference,
    private Obj mObj;

    @Override
    public SomeObj clone()
    {
        SomeObj obj = null;
        try
        {
            // 先做原物件的複製
            obj = (SomeObj)super.clone();

            // 再做物件成員的複製,
            // 假設 Obj 類別也有做好 clone()
            obj.setObj(mObj.clone());
        }
        catch(CloneNotSupportedException e)
        {
            e.printStackTrace();
        }
    }

    public void setObj(Obj obj)
    {
        mObj = obj;
    }
}
        有人可能會有疑問,原型模式跟抽象工廠模式都是生成物件,這兩個有什麼差別呢?其實很多時候生成物件的設計模式沒有很大的區別,就 client 的角度都是透過某個方法就能取得物件,但對於物件類別的架構而言,使用原型模式的話,子類別不用繼承父類別來達到其他功能 (Subclassing),但需要有初始化的行為 (總要先產生一個物件,之後才能用 clone 的);而抽象工廠模式通常是利用工廠方法來取得物件,而工廠方法需要 subclassing。

參考資料:

        深入淺出設計模式
        Prototype 模式
        Explanation of Deep and Shallow Copying

留言

這個網誌中的熱門文章

訪問者模式 (Visitor Pattern)

        假設你設計一個系統,其中會有一些相似類別,類別中都有某些方法內容相似,但還是需要判斷目前要做事的是哪個類別才能呼叫對應的適當類別。通常遇到這種情情,在 Java 中最直接的做法就是使用 instanceof 關鍵字來判斷,如以下的簡單範例: public interface CarComponent { public void printMessage(); } public class Wheel implements CarComponent { @Override public void printMessage() { System.out.println("This is a wheel"); } // 這是 Wheel 跟 Engine 不同的方法 public void doWheel() { System.out.println("Checking wheel..."); } } public class Engine implements CarComponent { @Override public void printMessage() { System.out.println("This is a engine"); } // 這是 Wheel 跟 Engine 不同的方法 public void doEngine() { System.out.println("Testing this engine..."); } } public class Car { private List mComponents; public Car() { mComponents = new ArrayList<carcomponent>(); } // 有些時候我們還是需要針對不同類別去做不同的事情 public void setComponent(CarCompon...

解譯器模式 (Interpreter Pattern)

        解譯器模式簡單來說就是 把一句有特殊規則的語句,透過解釋器將它真正的意思表現出來 。相信有學過 Context-free grammar (CFG),Backus–Naur form (BNF),或是 Compiler 相關程的人會比較了解這個模式 (不是我,我早就忘光了…)。不知道上面術語的人,還是可以透過接下來的介紹來稍微了解這個模式。先來看一下這個模式的正式定義及類別圖: 定義一個語言與其文法,使用一個解譯器來表示這個語言的敘述 Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language Context 通常是指待解譯的語句 AbstractExpression 是所有規則都要實作的介面 TerminalExpression 是指無法再展開的規則,算是最小單位的規則 NonterminalExpression 是指可以再展開的規則,可以展開成 NonterninalExpression 和 TerminalExpression 的組合         從類別圖可以看到,語法可能可以一直展開,這時就可以用語法樹來表示,而語法樹以程式來表達的話,就可以使用 合成模式 。而通常比較正式的語法會用 BNF 來表示,如下: expression ::= plus | minus | variable | number plus ::= expression expression '+' minus ::= expression expression '-' variable ::= 'a' | 'b' | 'c' | ... | 'z' digit = '0' | '1' | ... | '9' number ::= digit | digit number 而 每個語法都會定義一個類別 ,如 pl...

代理人模式 (Proxy Pattern)

        代理人模式如同字面上的意思,就是做事情時(如取得某些資料),是透過代理人,而不是直接跟提供資料的物件構通。先來看看此模式的正式定義及類別圖: 代理人模式讓某個物件具有一個替身,藉以控制外界對此物件的影響。 Provide a surrogate or placeholder for another object to control access to it.         從類別圖可以看到, Subject 是共用的介面,可以讓客戶 將 Proxy 物件視為 RealSubject 物件 來處理。RealSubject 是真正做事的物件,被 Proxy 代理, Proxy 可以控制 RealSubject 的存取 。Proxy 持有 RealSubject 的參考,客戶和 RealSubject 的互動都要透過 Proxy,在某些情形下這樣的限制是必要的,如 RealSubject 是遠端物件, RealSubject 建立成本高等等。         此模式有許多種變形,都是依據上面的原則而發展出來的。以下介紹  深入淺出設計模式(Head First Design Patterns) 裡面提到的一些應用方式:         1. 遠端代理人(Remote) :算是最常使用到的應用方式。在網路中或是跨 Process 的各種程式,不可能直接存取(assess) 不同程式間的物件,因此就需要遠端代理人。 Java RMI  和 Android AIDL  都算是遠端代理人的實踐。         2. 虛擬代理人(Virtual) :代理的對象是建立很花費資源的物件。當物件建立前和建立中,由虛擬代理人扮演代理的角色。物件建立完成後,代理人就會將讓求直接轉給物件。以下為簡單範例程式: // Proxy 實作跟 RealSubject 共同的介面 Icon public class ImageProxy implements Icon { // 這是我們的 RealSubje...