商業,創業,美食,葡萄酒,閱讀,網路科技。
這是我的 FB粉專 以及 IG,我比較常使用 Threads,歡迎大家追蹤互動~
本系列目標在於依照 [1] 這本經典所介紹的 Design Patterns 做一個 review 與整理。
本文我想先討論 [1] chapter 1 中 code reuse 的概念,接下來整理 Abstract Factory,Factory Method,Singleton,Builder,Prototype patterns。
在 [1] chapter 1 中 code reuse 主要分為三大類:Inheritance, Composition, Parameterized Types (Templates)。其中各自的優缺點是:
1. Inheritance
優:繼承關係程式碼可讀性高,架構容易理解。
缺:繼承關係是在 compile-time 就決定,flexibility 低。
缺:sub-class 有權更動 super-class 內部資料與行為,打破封裝。反之,super-class 的更動也會影響 sub-class 的行為。
2. Composition
優:可以在 run-time 再決定 composition objects,flexibility 高。
優:維持 composition objects 的封裝性。
缺:程式可讀性低。
3. Templates
compile-time 定義。
書中的意見是:Composition 是比 Inheritance 好的 reuse 技術,Templates 則提供了第三種選項。我個人認為:Inheritance 與 Composition 使用時機上並不同,有需要更動到 class 內部的要選用 Inheritance,無此需求的時候,當然就是選擇 Composition 了。
接著開始 5 個 design patterns 的討論。
1. Abstract Factory
首先以範例先描述 Abstract Factory。假設麥當勞的一號餐與二號餐會隨著夏季冬季作口味上的調整,則其模型可以如下:
以上例子可以勾勒出 Abstract Factory 的使用時機及好處:
- 生產 product (一號餐二號餐) 的責任完全與系統無關,系統甚至不須知道目前是夏季一號餐還是冬季一號餐,系統只要照著一般一號餐的管理與使用方式即可。以程式語言來說,系統與 factory 和 product 實作完全沒相依性,是很好的設計。
- 系統中所需物件可以被分類。以此範例來說,我們總共有夏季一號餐、冬季一號餐、夏季二號餐、冬季二號餐。很直覺的想法就是把他們分為夏季一組、冬季一組。當系統 configure 成夏季時即產生夏季的餐點,當系統 configure 成冬季時即產生冬季的餐點。
Abstract Factory 實作須知:
- concrete factory 應以 singleton 方式實作。
- Abstract Factory 新增 product 可以改為用參數指定的方式。以上面範例來說,假如要新增夏季三號餐,當然夏季麥當勞廚房要改變,另外麥當勞廚房、冬季麥當勞廚房也要一起變動。所以另一種方式是,兩個 method 生產一號餐與生產二號餐改為生產餐點(餐點號碼),這樣對單一 concrete factory 的擴充性會方便許多。
2. Factory Method
首先以範例先描述 Factory Method。
顧名思義,Factory Method 著重在 creation function 這個 method。Super-class 有一個 creation function,依照需求利用 sub-class 對此 creation function 做轉型。在未來開發而言,Factory Method 支援 parallel class hierarchies,這是很值得參考的做法。
Factory Method 實作須知:
- 同 Abstract Factory 第二項實作須知,參數化的 creation function 會有彈性很多。
- 使用 Factory Method 會容易產生過多的 sub-class (因為基本上它是與 product type 綁在一起)。此時 sub-class 可以以 template 的方式實作,理論上就會只有一個 template sub-class 了。
- [1]中提到 naming conventions。很多 framework 甚至規定 Factory Method 的 function name,使開發者一看就知道是 Factory Method。很多人可能認為這是小東西,但我個人認為軟體開發程式的基礎建設一定要做好,將來才可以是可管理、可測試的專案。
3. Singleton
Singleton 的範例我想直接用 sample code 來描述。
class Singleton
{
public:
static Singleton* getInstance( );
protected:
Singleton( );
private:
static Singleton* m_instance;
};
Singleton::m_instance = NULL;
Singleton* Singleton::getInstance( )
{
if( m_instance == NULL )
{
m_instance = new Singleton;
}
return m_instance;
}
Singleton 的觀念有兩點要注意的是
- Singleton 的作法其實就是 global variable 的改良。
- Singleton 中其實可以放多個 instance. 此時 Singleton 就會多一個管理器的角色。
Singleton 實作上還有很多變化,上述只是基本精神,實際應用上應隨著系統需求而靈活變化才是。
4. Builder
包含一個 director 和一個 builder。client 端擁有一個 director,builder 或以 reference 的形式存在在 director 端裡 (在 director construct 時),或是當參數傳入。
底下是 client 端的 sample code
// ...
MyDirector1 myDirector1;
MyWorldBuilder builder;
myDirector1.createWorld(builder);
MyWorld world = builder.getWorld( );
// ...
MyWorldBuilder
可能有 buildTrees
, buildHouses
, buildRivers
等 method。以上例來說,MyDirector1
生成了一種世界,MyDirector2
則會生成另一種世界,with the same MyWorldBuilder
使用時機方面,當最終需求的 product 有如樂高一般可以由不同的 component 所組成時,可以使用此 pattern。director 的 creation function 可以選擇 create product part A and B, 或是 B and C, 或是其他組合。
5. Prototype
Prototype pattern 最重要基本的精神就是使用 Clone method 把自己拷貝一份給 client。Client 需要 create prototype product 時,收到一個 prototype base class 的 pointer,調用 Clone method。
同樣是 run-time 決定要生成哪一個 concrete product 的 design pattern,Prototype pattern 與吃參數的 factory method 差別在哪?
以吃參數的 factory method 來說,client 會知道要生成哪一個 concrete product。因為通常 factory 會存在在 client 中,所以 factory method 的參數會經過 client。另外,client 也許也要確認 factory 是否支援此參數。
以 Prototype pattern 來說,client 完全不知到目前要生成哪一個 concrete product,也沒有要確認任何 concrete product 相關正確性的責任。
結論:Prototype pattern 會簡化系統 (client) 的設計與工作。但工作不會憑空消失,Prototype pattern 把相關的工作移到了類似 prototype manager 的模組去做。
在一般情形中,我個人還是會使用吃參數的 factory method。但在生成物件極度動態的情形下 (Ex: 依照 config 不同而不同,依照 branch 不同而不同),則會使用 Prototype pattern。
最後實作時要注意的一點是,C++ copy constructor 是 member-wise copy。但是 Clone method 所需的是 deep copy,實作時要稍微注意一下。
[1] “Design Patterns”, Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides.
商業,創業,美食,葡萄酒,閱讀,網路科技。