第231期 / January 5, 2017

分享到臉書!分享到維特!分享到噗浪!分享到Google+!分享到微博!轉寄友人友善列印

淺談常見軟體設計樣式(Design Patterns)

作者/柯景翔

[發表日期:2017/1/5]

簡介

談到設計樣式(Design Patterns),最廣泛為人討論與使用的莫過於Erich Gamma, Richard Helm, Ralph Johnson,John Vlissides四人幫(Gang of Four, GoF)所合著的《Design Patterns: Elements of Reusable Object-Oriented Software》,因此本文也由該書(以下簡稱GoF)作分析與介紹。

什麼是樣式(Pattern)呢?樣式就是一種反覆出現的行為模式,舉例來說:
「王子與公主,從此過著幸福快樂的日子。」童話故事的標準結尾。
「跳樓大拍賣!跳樓大拍賣!」老闆跳了三年還在跳樓。
「第一次XXX就上手!」人生總有許多第一次。
「淺談XXX」如本文標題所示。
諸如此類,反覆出現的特性。

樣式一詞最早來自建築師Christopher Alexander一段話“Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice”[註1],一般常見的定義為“A solution to a problem in a context”,在某個情境下解決特定問題的解決方案;另一方面,也有所謂的反面樣式(AntiPattern),它指的是低效又有待優化的設計樣式,即反覆出現卻似是而非的解決方法。

在GoF中的每個設計樣式,都是許多前輩在程式設計過程裡千錘百鍊,為了解決相關問題而歸納出來的核心經驗,藉此增加程式的標準化、靈活性、可重用性以及提高開發系統進度;設計樣式無關乎語言種類、僅僅只是作為設計導引,必要時可以依據開發語言和應用程式的特點作適當的調整,過度的設計(Over-Engineering)可能會使得系統更加複雜。

GoF所提出的樣式共有23種,並分成三大類:

一、創建型樣式(Creational)

“此類型模式試圖根據適合的情況來決定建立物件。單純的物件創建常會導致一些設計問題或增加設計的複雜度。創建型模式則藉由控制物件的生成方式來解決這問題。”[註2]

二、結構型樣式(Structural)

設計適用不同情境下的物件間關係結構,藉由一以貫之的方式來了解元件間的關係,以簡化設計。

三、行為型樣式(Behavioral)

“物件之間的合作行 為構成了程式最終的行為,物件之間若有設 計良好的行為互動,不僅使得程式執行時更有效率,更可以讓物件的職責更為清晰、整個程式的動態結構(像是物件調度)更有彈性。”[註3]


《表一》GoF的23種設計樣式


因為篇幅關係,下面僅對GoF所提出的這23個設計樣式中的工廠方法與抽象工廠樣式作介紹。

工廠方法與抽象工廠樣式介紹

一、工廠方法樣式Factory Method

在講工廠方法樣式之前,我們先來看看簡單工廠樣式(Simple Factory)

  • 簡單工廠樣式


  • 在設計與編寫程式時,我們經常會使用介面(Interface)將內部操作與外部溝通方法分離,使其能夠在修改實作時,而不影響到與外部實體互動,減少模組之間的耦合程度(Coupling)。

    我們常會保持基底類別(Base Class)的介面,並利用衍生類別(Derived Class)改變其實作,並選擇靜態繫結(Static Binding)直接在編譯時期指定實作方法、或是利用執行時期以基底類別(Base Class)指標動態繫結(Dynamic Binding)到實際實作的衍生類別(Derived Class)方法-多型(Polymorphism)等等,來符合實際的功能需求。

    也因此,我們可能會產生許多子類別,不得不在需要使用到該類別時,都寫上一行“... new XXXXX;”,這帶來幾個問題[註4]:
    • 工程師必須知道實際使用的類別名稱,當系統規模複雜起來,可能得面臨名稱衝突、可記憶性、可讀性等等問題,更不用說不同工程師的千奇百怪習慣。


    • 另一個問題是,我們可能在一個類別A需要使用到類別B的某個衍生類別,而類別A並不知道XXXXX究竟是哪個類別。

    了解決這些問題,因此產生了一個簡單工廠模式,範例如下(修改參考自 [註5]):假設我們是一家便當店(基底類別),有如下方法:


    《圖一》便當處理



    《圖二》便當店原始類別圖


    可以發現,其實兩種便當的處理動作很類似,所以我們可以將它拉出來封裝,一致化、並讓主菜實作該介面。


    《圖三》主菜基本處理介面



    《圖四》實作主菜基本處理介面之類別圖


    因此我們可以改變處理流程如圖五,可以看到,在調整過後,只需要對菜單的部份去做判斷即可,它的類別圖如圖六。


    《圖五》調整後做便當方法



    《圖六》便當店調整後的類別圖


    此時,我們知道若要更動菜單可能修改的地方為if-else部分,因此可以進一步修改圖五如下


    《圖七》主菜簡單工廠


    因為取主菜的工作獨立出來,此時處理流程可以修改如下,以減少便當店對主菜的相依性。


    《圖八》調整後做便當方法



    《圖九》便當店與簡單工廠


  • 工廠方法樣式


  • 在前文中,我們將主菜的部分委託主菜簡單工廠,只要有主菜更動需求,不用去修改便當店的任何方法,就可以增加新菜單;雖然如此,我們還是不可避免需要修改簡單工廠的方法,這動作違反了物件導向設計的OCP (Open Closed Principle)開放封閉原則,即開放擴充、禁止修改。

    因此我們將前文中的簡單工廠進一步封裝介面


    《圖十》主菜工廠介面


    之後我們依主菜建立工廠,如雞腿跟鯛魚便當抽象工廠:


    《圖十一》便當工廠


    此時,我們就符合OCP原則,調整後的便當工廠方法類別圖如圖十二所示。


    《圖十二》便當店與便當工廠


    二、抽象工廠樣式Abstract Factory

    在很多時候我們會希望工廠不只能生產單一物件,也能建立一系列相關的物件,此時就可能使用到抽象工廠樣式。例如我們希望鯛魚便當的品項上增加湯品,此時的便當工廠有可能的類別圖如圖十三所示,此工廠會生產鯛魚便當所需要的系列物件。


    《圖十三》鯛魚便當工廠


    同理,我們也可以類似的方式增加雞腿便當工廠,之後就可以視需要選擇適合的工廠來生產一系列相關的產品,如圖十四所示。

    同樣的需求下,我們也可以使用前述介紹的工廠方法樣式來實現,事實上,抽象工廠模式常常用工廠方法樣式來實作,“AbstractFactory classes are often implemented with factory methods”[1],如本例中的抽象便當工廠與XX便當工廠之間的關係。兩者的差異主要在於,前者主要為組合(composition)關係、後者主要在繼承(inheritance)關係,來達到同一個目的。


    《圖十四》抽象便當工廠


    三、工廠樣式的特性比較

    從前述文章內容,可簡單整理兩者樣式大致差異如下:


    《圖十五》


    可以看到工廠方法樣式結構較抽象工廠樣式簡單,若產品類不多,主要都是不相關聯的物件,此時我們可以考慮應用此樣式;反之,若需求是產出一群組合物件,則我們可考慮選擇以此模式創建與初始化。選擇的工廠樣式,取決於對產品等級劃分的程度。

    當然,有可能因為新需求稍加修改正使用的工廠方法樣式之後,而變成創建組合物件的抽象工廠樣式;另一方面,也有可能因為需求的變更,不再提供某些產品,而使得抽象工廠演變為工廠方法樣式,並無絕對的優劣選擇。

    結論

    從前述的兩個工廠樣式中,可以看到我們在設計系統時,可以避免在程式中的許多地方產生物件“... new XXXXX;”的困擾,也降低類別之間的耦合性,使得我們架構更加物件導向設計,並考慮到後續維護的成本。

    設計樣式是個非常廣泛的議題,Gof書上[1]及網路上(可參考[註6],Java、C++、PHP、Delphi)都有許多可供參考的原始碼;每種樣式都有其適合的情境,甚至有可能沒學過的人也無意中使用了某些樣式而不自知,當然,過度的設計也有可能落入反面模式(Anti-pattern或AntiPattern)[註7],有賴設計者們睿智的選擇。

    參考資料

    [註1] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, “Design Patterns: Elements of Reusable Object-Oriented Software”, Addison-Wesley, 1995.

    [註2] http://jimmy0222.pixnet.net/blog/post/37095632

    [註3] http://openhome.cc/Gossip/DesignPattern/

    [註4] 特羅特、沙洛韋、熊節,《設計模式精解》,清華大學出版社,2004年

    [註5] https://dotblogs.com.tw/joysdw12/archive/2013/06/23/design-pattern-simple-factory-pattern.aspx

    [註6] https://sourcemaking.com/design_patterns/adapter

    [註7] https://zh.wikipedia.org/wiki/%E5%8F%8D%E9%9D%A2%E6%A8%A1%E5%BC%8F