Agile Principles, Patterns, and Practices in C# 無瑕的程式碼 敏捷完整篇 第10章 LSP:Liskov替換原則

      第十章內容是LSP:Liskov替換原則,要遵守LSP才能有OCP的效果。


OCP背後主要機制是抽象和多型,C#支援抽象和多型的關鍵是繼承。如何建立適當的繼承關係,讓程式能夠符合OCP,答案就是LSP:Liskov替換原則。


Liskov 替換原則 (LSP)


子型態(subtype)必須能夠替換掉它們的基底型態 (base type)


假設有個函數f使用基底類別B,B有個衍生類別D,若D傳給f會產生錯誤行為,代表程式違反LSP。


若f的編寫者在D加入判斷條件修正錯誤,讓f能夠有正確的行為,這種作法違反OCP。 f對B的所有不同衍生類別都不再是封閉。


違反LSP的情形


一個簡單的範例


Listing10-1 基底類別Shape沒有寫好,每新增一個衍生類別就必須修改基底類別。


原因在於工程師不了解多型,誤認為多型會拖慢程式。


更微妙的違反情形


正方形繼承矩形的案例也會違反LSP。


繼承是IS-A的關係,正方形可繼承矩形。


正方形並不同時需要成員變數高度與寬度,但正方形仍然會從矩形繼承高度與寬度。原本接受矩形物件的函式,設定高度與寬度會讓正方形的高度與寬度改變,違反LSP。


可將setter屬性宣告為virtual可解問題,然而這樣做會影響基底類別,違反OCP。


Listing10-3是修改後的結果,基類別Rectangle寬度與高度設定為virtual ,衍生類別Square類別寬度與高度設定為override修改基底類別的寬度與高度。


真正的問題


接受矩形參數的函數g,輸入正方形還是會出現例外。


有效性並非本質屬性


這段大意是有沒有符合LSP要看宏觀整體,而不是微觀單一類別。Listing10-3 滿足矩形與方形的相容性,遇到函數g的設計者「對基底類別做出一些合理的假設」就會「破功」。


與第九章OCP情況相同,不要對未來過度猜想,出現狀況再改。


編者補充TDD測試能預測到未來一些變化。


IS-A是關於行為的


LSP指出繼承IS-A的關係是從行為來判斷,而行為可以進行合理假設。從行為的角度來看正方形不是矩形。


基於契約的設計 (Design by contract)


如何知道什麼是「合理的假設行為」?解決方法基於契約的設計DBC。


契約替每個方法宣告前置條件和後置條件。一個方法得以執行,前置條件必須為真,執行完後該方法要保證後置條件為真。


衍生類別必須和基底類別的所有後置條件一致。


在單元測試中指定契約


可透過撰寫單元測試來指定契約,可知道對於要使用的類別,應該做出什麼合理的假設。


一個真實世界的例子


動機


看圖10-2、Listing10-4與Listing10-5大約可知作者想要表達的內容。客戶可使用PrintSet函數不用關心是Unbounded Set 或是 Bounded Set 。


問題


參考圖10-3 Third-Party Persistent Set 只接受特殊的PersistentObject,Listing10-6 Add函數有可能會出現例外,客戶程式沒辦法知道它所加入的元素是否應該衍生自PersistentObject?


不符合LSP的解決方案


????????


只知作者用「契約」的方式解決,將PersistentObject與PersistentSet隱藏起來。


符合LSP的解決方案


參考圖10-4,承認PersistentSet與Set不存在 IS-A關係,PersistentSet與Set改當「兄弟」。


用提取共同部分的方法代替繼承


標題已經說明這段內容,Listing10-7 Line類別與Listing10-8 LineSegment類別的關係違反LSP。


解決方法將兩個類別共同部分提取出來成為一個超類LinearObject,讓Line類別與LineSegment類別繼承LinearObject超類。


啟發式規則和習慣用法


衍生類別通常功能都比基類別功能多,參考Listing10-13出現退化函數,有可能出現違反LSP的情況。


編者補充java版還有提到一種違反LSP的情況,衍生類別拋出基底類別不存在的例外。


總結


LSP是使OCP成為可能的主要原則之一。


術語IS-A含意過於廣泛,無法作為子型態的定義。子型態正確的意義應該是可替換的,可透過契約定義替換性。


 



留言

這個網誌中的熱門文章

異世界NTR web版第三章 觀後感 喧賓奪主 ,反派實力過強

泛而不精的我被逐出了勇者隊伍 web第三章 觀後感 菲莉真能打; 露娜超爽der

持有縮小技能的D級冒險者,與聖女結婚並加入勇者團隊 漫畫 01-04 觀後感 大我與小我