編碼最佳實踐——開放封閉原則


mark

開放封閉原則定義

開放與封閉原則有兩種不同的定義,分別是20世紀80年代最原始的定義和后期一個更現代的定義,后者對前者進行更加詳盡的闡述。

Meyer的定義

軟件實體應該允許擴展,但禁止修改

​ ——《面向對象軟件構造》

Martin的定義

”對於擴展是開放的。“ 這意味着模塊的行為是可以擴展的。當應用程序的需求改變時,我們可以對其模塊進行擴展,使其具有滿足那些需求變更的新行為。換句話說,我們可以改變模塊的功能。

“對於修改是封閉的。“ 對模塊行為進行擴展時,不必改動該模塊的源代碼或二進制代碼。模塊的二進制可執行版本,無論是可鏈接的庫、DLL或Java的.jar文件,都無需改動。

​ ——《敏捷軟件開發:原則、模式與實踐》

對於修改是封閉的

需要注意的是,“對於修改是封閉的”有兩個例外:

1.修復缺陷所做的改動

2.客戶端無法感知到的改動

缺陷修復

缺陷在軟件中很常見,是不可能完全消除的。當缺陷出現時,就需要我們修復現有的代碼。軟件修復明顯傾向於實用主義而不是堅持開放封閉原則。

客戶端感知

如果一個類的改動會引起另一個類的改動,那么這兩個類就是緊密耦合的。相反,如果一個類的修改總是獨立的,並不會引起其他類的改動,那么這些類就是松散耦合的。我們要記住,任何情況下,松散耦合都比緊密耦合要好。如果我們對現有代碼的修改不會影響客戶端代碼,那么也就談不上違背開放封閉原則。

對於擴展是開放的

擴展點

沒有擴展點

mark

TradeProcessorClient類直接依賴TradeProcessor類。當接到一個需要改動TradeProcessor類的新需求時,為了不改變原有的類型,創建了一個新類型(TradeProcessor2)來實現需求提出的新功能。但是這種改動帶來的副作用就是必須改動TradeProcessorClient類,這樣才能依賴的新的TradeProcessor2類。

如果對現有代碼的改動不會影響客戶端,那就不需要創建新類型。但是如果對現有代碼的改動改變了TradeProcessor類方法的簽名,那就不是簡單的對類實現的改動,而是對接口的改動了。因為客戶端總是與服務的接口緊密耦合的,所以任何接口上的改動都會引起客戶端代碼的改動。

虛方法

TradeProcessor類的另一種實現包含了一個擴展點:ProcessTrades是個虛方法。

任何一個帶有虛方法成員的類都是對外開放的,這種擴展是通過繼承做到的。可以修改其子類的ProcessTrades方法而無需改變原有的TradeProcessor類源碼。此時的TradeProcessorClient類也不需要做改動,可以使用多態向客戶端提供新版本的TradeProcessor2類的實例。

但是使用虛方法能重新實現的范圍是有一定限制的。在子類中可以訪問基類,因此可以直接調用TradeProcessor類的ProcessTrades方法,但是無法改動該方法內的任何代碼。要么在子類方法里調用基類同名方法並在其前后實現新的特性,要么完全重新實現子類的方法。虛方法沒有中間狀態。另外子類只能訪問基類的受保護和公共成員,如果基類中有很多子類無權訪問的私有成員,可能就需要修改基類的實現了。但是,這又會違背開放封閉原則。

mark

抽象方法

另外一種使用實現繼承的更加靈活的擴展點是抽象方法。

客戶端依賴抽象基類,因此提供任何一個具體子類(或者用來支持新需求的子類)給客戶端都不會違背開放封閉原則。

mark

接口繼承

最后一個擴展點是實現繼承外的另外一種選項:接口繼承。客戶端委托接口取代了客戶端對類的依賴。

接口繼承要比實現繼承好很多。基於實現繼承,所有子類(現有的和將來的)都是基類的客戶端。給繼承圖頂部節點添加新成員的改動會影響到該層級結構下的所有成員,而接口要比類靈活的多。這當然不是說代表實現繼承的虛方法和抽象方法提供的擴展點沒有一點用處,但是它們的確無法提供與接口一樣強大的自適應能力。

mark

防止變異

雖然我們已經知道了實現擴展點的方式,但是我們應該到處都留着擴展點嗎?防止變異是另外一個跟開放封閉原則相關的重要准則:

識別可預見的變化點並圍繞它們創建一個穩定的接口。

可預見的變化

要識別出很可能發生變更的需求或者實現起來特別麻煩的代碼部分,然后將它們隱藏在擴展點之后。

一個穩定的接口

依賴接口的最大優勢是接口變化的可能性要比實現小很多。用於表達擴展點的所有接口應該都是穩定的。因為客戶端是直接依賴接口的,如果接口發生變化,客戶端也必須做相應的改動。

最后

通過確保代碼對擴展開放對修改封閉,可以有效阻止后期變化對現有類的修改,因為后面的編碼人員只能在你預留的擴展點上掛靠新創建的類。代碼可以很死板,幾乎無法擴展和細化;代碼也可以很流暢,帶有足夠的准備應對新需求的大量擴展點。兩種選擇都沒有錯,只是要在具體的場景進行選擇和應用。

參考

《C#敏捷開發實踐》

作者:CoderFocus

微信公眾號:

聲明:本文為博主學習感悟總結,水平有限,如果不當,歡迎指正。如果您認為還不錯,不妨點擊一下下方的推薦按鈕,謝謝支持。轉載與引用請注明作者及出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM