Louis Better than before

淺談物件導向的基本概念(II)

“Object-oriented programming (OOP) is a programming paradigm based on the concept of “objects”, which can contain data and code: data in the form of fields (often known as attributes or properties), and code, in the form of procedures (often known as methods). Many of the most widely used programming languages (such as C++, Python, etc.) are multi-paradigm and they support object-oriented programming to a greater or lesser degree, typically in combination with imperative, procedural programming. Significant object-oriented languages include: C++, Python, Perl, MATLAB … etc. - wiki”

延續上一篇文章,接續整理Design Patterns Explained: A New Perspective on Object-Oriented Design這本書的筆記。

設計模式簡介

設計模式(Design Pattern)是物件導向設計的其中一項進展,這專業術語是由Erich Gamma等人在1990年代從建築設計領域引入到計算機科學,之後於2011年加入Microsoft。在軟體工程中,設計模式可對軟體設計中普遍存在或反覆出現的各種問題,提出要怎麼解決問題的一種方案,而背後的一個觀點,就是軟體系統的品質存在共識的客觀模式來度量或評價。舉例來說,兩個建築中的門檻結構上也許不同,但是,它們是為了解決相同或相似的問題(“區分入口”)而設計的不同結構或是解決方案,因此這兩種不同門檻結構設計之間的相似之處(“區分入口”)可以稱之為模式,並且可能也都具備高品質。換句話說,每個模式都描述了一個環境中會不斷重複出現的問題,並進而敘述了這個問題解決方案的要素,透過這種方式,解決方案能夠百萬次地反覆應用,但是具體方式又不會完全相同。一般性而言,任何設計模式的描述都應該包含下列幾個項目:

項目 描述
名稱 每個模式都有唯一的用於標識的名稱。
意圖 模式的目的。
問題 模式要解決的問題。
解決方案 模式怎樣為問題提供適合其所處環境的一個解決方案。
參與者和協作者 樣式所涉及的實體。
效果 使用模式的效果,研究模式中發揮作用的各種因素。
實作 樣式的實作方式。
一般性結構 顯示模式典型結構的標準圖。

設計模式的優點:

  • 可重復性使用的解決方案 - 透過公認的設計,能夠解決問題時取得先發優勢,而且避免“重蹈前人覆轍” (可以從學習他人的經驗中獲益,用不著為那些總是會重複出現的問題再次設計解決方案)
  • 確立通用術語 - 對於開發項目的分析和設計階段提供需要共同的詞彙基礎和對問題的共識
  • 使程式更易維修 - 大多數的設計模式都是久經考驗的解決方案,所以結構都是經過長期發展形成的,比新構思的解決方案更善於因應變化

值得一提,模式只是提出了通用方法,是否增加新的功能應根據具體情況而定。換句話說,模式只是可以作為起點的藍圖,可不能完全原照搬。

設計模式

設計模型類型

  1. Facade Pattern
  2. Adapter Pattern
  3. Strategy Pattern
  4. Bridge Pattern
  5. Singleton Pattern

Facade Pattern

Facade pattern為子系統中的一組介面定義一個統一介面,使子系統更加容易使用,其關鍵特徵如下:

項目 描述
意圖 希望簡化原有系統的使用方式。需要定義自己的介面。
問題 只需要使用某個複雜系統的子集,或者,需要以一種特殊的方式與系統交流。
解決方案 Facade為原有系統的客戶提供了一個新的介面。
參與者和協作者 為客戶提供的一個簡單介面,使系統更容易使用。
效果 Facade模式簡化了對所需子系統的使用過程。
實作 定義一個(多個)具備所需介面的新類別,讓新的類別使用原有的系統。

Facade Pattern結構圖的範例:

Facade pattern可以用來隱藏或者封裝系統,透過Facade類別將系統作為自己的私有或是公開成員包含進來,在此情況下,原系統將與Facade類別關聯起來,未來若需要切換系統,可以用最省力地切換到新的系統。

Adapter Pattern

Adapter pattern將一個類別的介面轉換成客戶希望的另外一個介面,使原本由於介面不相容而不能一起工作的類別可以一起工作。

項目 描述
意圖 使控制範圍之外的一個原有物件與某個介面匹配。
問題 系統的資料和行為都正確,但介面不符。通常用於必須從抽象類別衍生時。
解決方案 Adapter模式提供了具有所需介面的wrapper(包裝)類別。
參與者和協作者 Adapter改變了Adapter的介面,使Adapter與Adapter的基礎類別匹配。
效果 Adapter模式使原有物件能夠適應新的類別結構,不受其介面的限制。
實作 將原有類別包含另一個類別之中。讓包含類與需要的介面匹配,呼叫被復合類別的方法。

Adapter Pattern結構圖的範例:

(Adapter)

(Adaptee)

從某種層次上來看,Facade pattern和Adapter pattern的確很類似,它們都是wrappers(包裝)。但是,它們是不同類型的wrappers(包裝)。換句話說,Facade pattern簡化了介面,而Adapter pattern則將一個已有的介面轉換成另一個介面。

Strategy Pattern

Strategy pattern定義一系列的演算法,把它們一個個封裝起來,並且使它們可相互替換。換句話說,使演算法可獨立於使用它的客戶而變化。

項目 描述
意圖 可以根據所處上下文,使用不同的業務規則或演算法。
問題 對所需演算法的選擇取決於發出請求的客戶或者要處理的資料。
解決方案 對於演算法的選擇和演算法的實作相分離。允許根據上下文進行選擇。
參與者和協作者
  • Strategy指定了如何使用不同的演算法。
  • 各ConcreteStrategy實作了這些不同的演算法。
  • Context將來自Client的請求轉發給Stragegy,選取特定的演算法。
  • 效果
  • Strategy模式定義了一系列的演算法。
  • 可以不使用switch語法或條件陳述式。
  • 必須擁有相同的介面以相同的方式呼叫所有演算法。
  • 實作
  • 讓使用演算法的類別(Context)包含一個抽象類別(Strategy),該抽象類別有一個抽象方法指定如何呼叫演算法。
  • 每個衍生類別依需要實作演算法。
  • 選擇所用具體實作的職責由Client物件承擔,並轉給Strategy模式的Context物件。
  • Strategy Pattern結構圖的範例: (ConcreteStrategy)

    (Strategy)

    從技術角度而言,Strategy模式就是用來封裝演算法的,而且有助於降低單元測試成本,因為每個演算法都有自己的類別,可以透過自己的介面單獨測試。至於,Context與Strategy之間的耦合以及各種相互作用,使其不便於做單元測試。

    Bridge Pattern

    Bridge pattern將抽象與實作解耦合,使它們都可以獨立地變化。這意味著可以在不改變實作的情況下增加新的抽象類別,反之亦然。

    項目 描述
    意圖 將一組實作與另一組使用它們的物件分離。
    問題 一個抽象類別的衍生類別必須使用多個實作,但不能出現類別爆炸性增長。
    解決方案 為所有實作定義一個介面,供抽象類別的所有衍生類別使用。
    參與者和協作者
  • Abstraction為要實作的物件定義介面。
  • Implementation為具體的實作類別定義介面。
  • Abstraction的衍生類別使用Implementaion的衍生類別,卻無需知道自己具體使用哪一個ConcreteImplememtion。
  • 效果 實作與使用實作的物件解耦,提供了可擴展性。
    實作
  • 將實作封裝在一個抽象類別中。
  • 要在實作中的抽象基礎類別中包含一個實作的控制碼。
  • Bridge Pattern結構圖的範例:

    (Bridge)

    (Implementation Interface)

    在真實世界中,不會總是一開始就有很多實作,一種辦法就是總是使用抽象類別,為多個實作做好準備,這種辦法編寫程式碼是很重要的,因為如此修改這些程式碼以採用bridge模式並不困難。修改程式碼改進結構但不增加新功能,就是所謂重構(refactoring)。

    Singleton Pattern

    Singleton Pattern用於單執行緒應用程式中,確保一個類別(class)僅有一個實體(instance),並提供一個存取它的全域(global)存取點。其工作原理是:用一個特定方法來實體化所需的物件,並且

    • 呼叫此方法時,檢查物件是否已經實體化。如果已經實體化,此方法僅返回對該物件的一個參照(reference)。如果尚未實體化,此方法則實體化該物件並返回對此新實體的一個參照。
    • 為了確保實體化此類型物件是唯一的方法,將這類別的建構(constructor)函數定義為保護(protected)或私有(private)。
    項目 描述
    意圖 希望物件只有一個實體,但沒有控制物件實體化的全域物件。還希望確保所有實體使用該物件相同的實體,而無須將參照傳給它們。
    問題 幾個不同的Client物件需要參照同一物件,而且希望確保這種類型的物件數目不超過一個。
    解決方案 保證一個實體。
    參與者和協作者 Client物件只能透過getInstance方法建立Singleton實體。
    效果 Client物件無須操心是否已存在Singleton實體。這是由Singleton自己控制的。
    實作
  • 增加一個類別的私有靜態成員變數,參照所需的物件(初值為null)。
  • 增加一個公共靜態方法,它在成員變數的值為null時,實體化這個類別(並設定成員變數的值),然後返回該成員變數的值。
  • 將建構函數的狀態設定為保護或私有,而防止任何人直接實體化這個類別,繞過靜態建構函數機制。
  • Singleton Pattern結構圖的範例:

    另外值得一提的是在多執行緒程式中,Singleton Pattern可能會出現一些不預期的問題或是不能總是正常運作。例如:兩個執行緒都執行Singleton物件的new操作,因此建立兩個物件,或是導致執行程式過程中,出現memory leak的問題。因此,若是在多執行緒程式中,可採用Double-Checked Locking Pattern。

    =========== To be continued…. ==========

    Reference

    [1] Wiki: 設計模式 (電腦)

    [2] Design Patterns Explained: A New Perspective on Object-Oriented Design

    [3] Design Patterns in Java Tutorial

    [4] Radek’s Blog

    Thanks for reading! Feel free to leeve the comments below or email to me. Any pieces of advice or discussions are always welcome. :)