使人瘋狂的 SOLID 原則:開放封閉原則 (Open-Closed Principle)

YC
程式愛好者
Published in
Aug 26, 2020

今天我們要說的是第二個原則:開放封閉原則(OCP)

相比第一篇 SRP 原則來說,開放封閉原則可以說是好懂三百倍了。

定義:一個軟體製品應該對於擴展是開放的,但對於修改是封閉的。

這樣的中文定義老實我個人認為邏輯不夠嚴謹。這樣的定義是指「一個軟體應該對於擴展是開放的一個軟體應該對修改是封閉的。」還是「一個軟體製品在面對擴展時是開放的,且擴充時不應修改到原有的程式。」呢?

維基百科的定義是:
系統一旦完成,一個類的實現只應該因錯誤而修改,新的或者改變的特性應該通過新建不同的類實現。

在 Uncle Bob 的文章中,他定義為:
You should be able to extend the behavior of a system without having to modify that system.

我們清楚理解到這是一個「且」的關係,即:

一個軟體製品在面對擴展時是開放的,且擴充時不應修改到原有的程式。

對,就是這麼簡單!

當年他們會提出這樣的原則是有理由的,比如說 Linux Kernel,他們盡量不希望在為一個穩定的 Linux Kernel 版本新增功能時,因為會改動到原有的程式碼而使 Kernel 也要做出大量的修改了,然後出現很多新的 Bug。而是希望在新增功能時,原有穩定的版本還是保持一致,否則 debug 起來就是地獄。

我們可以看到最美妙地遵守 OCP 的幾個常用程式,如 Google Chrome、VSCode等等。我們可以輕易為他們新增很多的 plugin,且不會對原來的主體造成影響。

那怎樣才能達成 OCP?簡單來說,透過 SRP 我們可以就不同因素而改變的模組分類好,再透過 DIP (依賴反向,之後的文章我們會更詳細的說到)來為系統創建一個單向的流程。

具體來說, 我們在說架構時都會把程式分成很多層。而在系統中的最高層,通常都是業務邏輯層,其他層次都是圍繞著業務邏輯層而進行分工。而這種分工比較像以下的架構圖:

在上圖中,整個架構的核心是 Interactor (業務邏輯層),他會被 Controller 與 DB 所依賴。而 Presenter 又會依賴於 Controller 。

這層層的單向依賴有效於解耦。

在軟體設計中,元件不應依賴於不會直接使用到的東西,如 Interactor 不會直接使用到 View, Interactor 跟 View 之間當然不應有著依賴的關係。同理地,作為底層的元件只需做好自己的「本份」,如 View 就應該是只處理視圖的邏輯,不應也不需要知道 Presenter 是在處理什麼邏輯,這樣的單向依賴可以讓高層元件免受非有依賴關係的低層元件改變影響。

所以在設計架構時,我們要根據如何、為什麼及何時發生變更來分離功能,然後將分離的功能組織到元件階層中。

說了這麼多,OCP 不是要告訴我們程式該怎麼分層、工作該怎麼寫好,而是給出一個解耦的概念,我們應該要朝著就算加了三四五六七個新的功能,我們還是不會影響到原有的程式,更能優雅地設計著!

優雅地設計一個「即使需要功能擴充,亦不需要修改原程式碼的系統」。而這樣的目標就有賴於我們怎麼樣設計我們的程式架構了!

如果你覺得我的文章幫助到你,希望你也可以為文章拍手,分別 Follow 我的個人頁與程式愛好者出版,按讚我們的粉絲頁喔,支持我們推出更多更好的內容創作!

--

--

YC
程式愛好者

提供更精確的技術內容為目標,另創立「程式愛好者」專頁。資深軟體工程師,專研後端技術、物件導向、軟體架構。