Skip to main content

開放封閉原則

定義

Software entities like classes, modules and functions should be open for extension but closed for modifications.
軟體在生命週期內,應該對擴展開放,對修改封閉。

說明

既然發生變化是一件必然會發生的事情,在設計的時候將這些變化的適應能力設計到專案上面,讓我們面對變化可以更有餘力去解決。而開放封閉原則,就是希望能夠盡量擴展軟體的實際行為,而不是透過修改現有的程式碼來完成變化。

一般來說,可以把變化歸類為下面三種

  1. 邏輯變化
    只變化一個邏輯,不影響其他子模組的,前提是所有關聯的模組或是依賴,都必須以相同的邏輯處理。
  2. 子模組變化
    一個模組變動,必然會對其他的模組產生影響,特別是低層次的模組變化必然會導致高層次模組的變化,因此透過擴展修改時,高層次模組的修改是依定會發生的,甚至會讓使用者介面發生影響。
  3. Client API / UI 變化
    這邊指的是提供給客戶使用的介面。

實作建議

開放封閉原則也僅是一個原則,要實現這個原則也並非限於這幾種設計原則和方法,但是遵循這六大原則 (SOLID),基本上可以應付絕大多數的變化,建議以這六大原則為基礎,然後在必要的時候再進行擴充。

實例

需求

// ...

設計

// ...

效益 & 注意事項

效益

  1. 對測試的影響
    所有在 Production 的程式碼都是有意義的,上線前都應該受到嚴格的驗證和系統規則約束,這樣經過千錘百鍊測試的程式碼,是否有辦法在不修改的前提下,僅透過擴展實現功能變化。如果不行,那表示需要把原有的測試項目全部在執行一次,光是想到這樣的情境,是不是就讓人覺得不想去改原本的程式碼。

    如果通過擴展的方式,例如說新增一個子類別完成業務需求的擴展功能,只要針對這個新類別去撰寫功能和新增測試項目就好,既有的測試項目和功能都不會被調整到。在動手上就不會擔心異動所關聯的地方太多,導致連帶發生的影響。

  2. 提高可重用姓
    在物件導向設計中,所有的邏輯都是透過許多粒度小的功能組合而成。只有當力度縮小到不可在分割時,才可以避免類似的邏輯散落在各個地方,導致要新增功能時要一直翻找類似功能的程式碼做參考,無法用現有的模組進行開發。

注意事項

  1. 抽象限制
    抽象是對事物的通用描述,沒有具體的實現細節,表示本身可以有非常多的可能性,可以根據需求變化而異動。因此透過介面或是抽象類別,可以限制可能變化的行為,並且能夠實現對擴展開放。

    • 通過介面或是抽象類別對擴展進行邊界限制,不允許出現不存在於介面或是抽象類別的公開方法。
    • 參數型別盡量使用介面或是抽象類,避免直接依賴實作。(依賴反轉)
    • 抽象類別或是介面,盡量保持穩定,一旦確定盡量不要調整。
  2. 封裝變化
    對變化的封裝包含兩層涵義

    • 將相同的變化封裝到一個介面或是抽象類別
    • 將不同的變化封裝到不同的介面或是抽象類別,不應該有兩個不同變化的原因出現在同一個介面或是抽象類別上。

    找出預計可能發生變化的點,就可以進行封裝,而後面要介紹的設計模式,就是從不同角度對變化進行封裝。

Reference

設計模式之禪