l

2012年10月5日 星期五

Creational Patterns要解決什麼問題(上)?

Oct. 05 12:30~14:22

image

這一陣子Teddy在重新整理GoF的23個Design Patterns,當整理到5個creational pattern的時候,Teddy突然想到兩個問題:

  1. 這5個creational pattern到底是要解決什麼問題?
  2. 它們之間的差別是什麼?

首先看一下GoF書中對於這5個creational pattern的Intent:

  • Abstract Factory: Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
  • Builder: Separate the construction of a complex object from its representation so that the same construction process can create different representations.
  • Factory Method: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
  • Prototype: Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
  • Singleton: Ensure a class only has one instance, and provide a global point of access to it.

仔細一看這5個pattern的Intent都蠻像是Solution的,所以要找出這5個pattern所要解決的問題,就必須自己歸納一下。既然這5個pattern被分類在creational pattern,所以它們要解的的問題,一定是和「產生物件」有關。因此,寫先出第一版的問題。

Problem V1:How do you create an object? (你如何產生一個物件?)

第一版的問題算是一個很一般化且共通的問題,可以適用於這5個creational pattern。但是,光是這樣不足以區隔這5個pattern,也就無法推導出針對這5個pattern,要採用哪種Solution。請問鄉民,如果這5個pattern的問題都一樣,要如何區隔它們?

***

在回答這個問題之前,先來整理一下,平常寫程式的時候,有用過哪些方式來產生一個物件?以下以Java語言為例子:

  1. 直接在需要產生物件的時候把物件給new出來,例如 List<String> list = new ArrayList<>();
  2. 用Reflection的方式來產生物件。
  3. 用clone(Prototype pattern)的方式來產生物件。
  4. 用Singleton pattern。
  5. 用Object Pool pattern。
  6. 用Simple Factory pattern。
  7. 用Factory Method pattern。
  8. 用Abstract Factory pattern。
  9. 用Builder pattern。

以上九種不同的方式,都回答了上面這個根本的問題How do you create an object?

為什麼產生物件這麼簡單的問題,至少就有9種不同的方法?是什麼因素導致於一個問題衍生出9種不同的解法?

***

Teddy在《從 The Timeless Way of Building 學設計 (1)》中提到,設計是一種「差異化」的過程。從一個共同的問題作為出發點,現在來探討一下是什麼「因素」(Force)演化出不同的 物種 Solution出來。今天先看幾個簡單的解法。

直接把物件new出來的Force

  • 你在設計階段已經知道要使用哪種具體類別的物件。
  • 所使用的物件很固定,不需要因為執行環境不同而有所變化。
  • 你認為將產生物件的程式碼直接寫在使用物件者身上比較簡單且容易理解。

使用Reflection的Force

  • 在設計階段你只知道所需使用物件的介面,無法決定實做的具體類別。
  • 希望在執行階段能夠動態決定所產生物件的具體類別,以增加系統的彈性。
  • 你認為將產生物件的程式碼直接寫在使用物件者身上比較簡單且容易理解。

Simple Factory的Force

  • 在設計階段你只需要知道所需使用物件的介面,不關心實做的具體類別。
  • 為了降低系統的相依性,你希望物件的使用者不需要知道產生物件的過程。
  • 將產生物件的責任集中處理,當產生物件的過程有異動的時候,會比較容易管理。

***

以下簡單用一個圖來說明上述的觀念。要套用pattern的時候,出發點都是從Problem開始。你現在要產生一個物件,哪麼要用那種方式來產生物件呢?

螢幕快照 2012-10-05 下午2.01.24

 

  • 如果你在寫程式的當下就知道要用哪種物件,例如ArrayList或是LinkedList,而且一旦決定之後日後也不太可能會變動,那就直接用new的方式把物件給產生就好了。
  • 如果你只知道你要使用的物件的介面,但是當程式執行的時候(runtime)才能夠決定要使用哪一個具體的物件(可能是透過讀取設定檔的資料或是讓使用者決定),那麼你就可以透過Reflection的方式來產生物件。例如,你要設計一個Plugin程式,你事先定義好了Plugin的介面,但是你並不知道系統真正執行的時候,使用者會安裝那些Plugin,所以你不可能在寫程式的時候就用new的方式把這些使用者安裝的Plugin給產生出來。這時候可以透過Reflection的機制,當程式執行的時候,先查看使用者安裝了那些Plugin,然後用以下的方式產生Plugin物件。

    Class<?> c = Class.forName("YourPluginName");
    Plugin plugin = (Plugin) c.newInstance();

  • 前面兩種方法都把產生物件的責任交給使用物件的人,有時候我們希望使用物件的人不用管物件是怎麼產生的,只要知道需要產生物件的時候,要找誰要就可以了,這時候就可以使用SimpleFactory。以剛剛Plugin的例子,我們可以寫一個新的PluginFactory類別,讓他來幫忙產生Plugin。

public class PluginFactory {

    public Plugin createPlugin(String PluginName){

         // 透過Reflection產生Plugin

    }

}

然後原本需要使用Plugin的物件就不用自己透過Reflection來獲得Plugin物件,而是改透過PluginFactory這個SimpleFactory就可以了。

          Plugin p = PluginFactory.createPlugin(“MyPlugin”);

***

友藏內心獨白:有沒有慢慢體會到「設計是一種差異化的過程」的fu?

沒有留言:

張貼留言