今天這一篇,要介紹的是開放封閉原則,也就是Open/Close Principle。
開放封閉原則,可以說是SOLID的總綱,也是任何一個物件導向設計,期望遵循的原則。
1.定義
(1)軟體設計原則,應該對擴展開放,對修改封閉
(2)原文定義: Software entities like classes, modules and functions should be open for extension but closed for modifications.
2.簡單的說
簡單的說,一個class, function, modules在碰到需求變更或新增時,應該可以只增加code,而不去修改原本的code,就能滿足變化
3.不改code,那幾乎是不可能的
完全不用改code,是不太可能的,但是我們改的部分,應該只有組合或mapping concrete class的部分,而不是concrete class或function的內容。
4.為什麼要用開放封閉原則
Open/Close Principle其實比較像是SOLID裡面另外五個原則的總綱。帶來的好處有下列幾點:
(1)可測試性
當使用抽象介面將場景與concrete class隔開之後,我們就可以寫測試程式,透過簡單的OO方式與test framework的輔助,來模擬抽象介面背後的concrete class會具備什麼樣的行為
(2)重用性
要對修改封閉,擴展開放,通常就需具備高內聚力,也就是class與interface等粒度應該是,在符合需求與未來改變的前提下,粒度切到最小。粒度越小,重用性越高。
(3)可維護性
當不用去改舊的、已經存在的code,不用trace那老太婆的裹腳布的前人遺毒時,只需要自己撰寫新的code符合需求,然後隨插即用,當然是件快樂的事。不只可以提高可維護性,還可以改善維護人員的心情呢。
(4)擁抱改變
當面對改變時,都可以透過新增class來擴展原本的系統,不用有太多的包袱以及過多的擔憂,當然就能設計出類似真實世界隨時在改變的SOLID system。
5.如何實現
(1)抽象約束
透過abstract或interface,在run time的時候,才連接實際concrete class。也就是多型的應用,IoC的概念,低耦合的實踐。
在實際的運用場景,我們只需要專注在場景上要使用到的抽象介面,透過該介面提供的行為來組成與執行商業邏輯。
這樣場景的設計,才會抽象。不管實作內容(class或class的function)如何變化,都會且必須滿足場景的抽象使用。
(2)透過抽象介面當作接口,來設計場景。當需求變更時,該如何達到對擴展開放,對修改封閉呢?
(a)只異動metadata即能擴展系統
可以透過factory pattern或Dependency Injection,經過參數或reflection來達到使用新的concrete class完成新的需求。
而不需要直接修改舊的class內部邏輯,也不需修改場景的商業邏輯,只需要新增新的concrete class,且將mapping指向新增的concrete class即可。(可運用strategy patten)
(b)另一種偷吃步的方法,是使用繼承。透過繼承與覆寫父類別的方法,讓實際run time時,由子類別來實作父類別的功能,但新的需求與設計內容,則由子類別決定。
這樣也可以達到,只有新增子類別,不改父類別內容,場景只修改run time的執行instance為子類別,就達到開放封閉原則的標準。
[注意!!]但這是一種治標不治本的方式,如同之前文章講的,繼承是一種高耦合的行為,當這個系統要長久運作與維護,因為偷吃步而產生的繼承鏈長度,出來跑,總有一天要還的。
(3)封裝變化
設計的時候,應該將可能變化或不穩定的部分,封裝起來,以抽象介面隔開。透過Design patterns來封裝變化。
================================
最後,請想學習的客倌,看完這篇文章思考一下,下列的問題該如何回答:
1.什麼是開放封閉原則?
2.可以用哪些方式來設計符合開放封閉原則的系統?
3.開放封閉原則的好處是啥?