天天看點

面向對象設計的基本原則之--裡氏替換原則

參考原文:

https://blog.csdn.net/zhengzhb/article/details/7281833 

https://blog.csdn.net/lovelion/article/details/7540445

裡氏代換原則是面向對象設計的基本原則之一。 裡氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。LSP 是繼承複用的基石,隻有當派生類可以替換掉基類,且軟體機關的功能不受到影響時,基類才能真正被複用,而派生類也能夠在基類的基礎上增加新的行為。裡氏代換原則是對開閉原則的補充。實作開閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關系就是抽象化的具體實作,是以裡氏代換原則是對實作抽象化的具體步驟的規範。

裡氏代換原則(Liskov Substitution Principle, LSP):所有引用基類(父類)的地方必須能透明地使用其子類的對象。

    裡氏代換原則告訴我們,在軟體中将一個基類對象替換成它的子類對象,程式将不會産生任何錯誤和異常,反過來則不成立,如果一個軟體實體使用的是一個子類對象的話,那麼它不一定能夠使用基類對象。例如:我喜歡動物,那我一定喜歡狗,因為狗是動物的子類;但是我喜歡狗,不能據此斷定我喜歡動物,因為我并不喜歡老鼠,雖然它也是動物。

      例如有兩個類,一個類為BaseClass,另一個是SubClass類,并且SubClass類是BaseClass類的子類,那麼一個方法如果可以接受一個BaseClass類型的基類對象base的話,如:method1(base),那麼它必然可以接受一個BaseClass類型的子類對象sub,method1(sub)能夠正常運作。反過來的代換不成立,如一個方法method2接受BaseClass類型的子類對象sub為參數:method2(sub),那麼一般而言不可以有method2(base),除非是重載方法。

      裡氏代換原則是實作開閉原則的重要方式之一,由于使用基類對象的地方都可以使用子類對象,是以在程式中盡量使用基類類型來對對象進行定義,而在運作時再确定其子類類型,用子類對象來替換父類對象。(感覺有點像上轉型對象)

問題由來:有一功能P1,由類A完成。現需要将功能P1進行擴充,擴充後的功能為P,其中P由原有功能P1與新功能P2組成。新功能P由類A的子類B來完成,則子類B在完成新功能P2的同時,有可能會導緻原有功能P1發生故障。

解決方案:當使用繼承時,遵循裡氏替換原則。類B繼承類A時,除添加新的方法完成新增功能P2外,盡量不要重寫父類A的方法,也盡量不要重載父類A的方法。

比如我父類有一個減法的方法f(),然後我要新增一個加法的方法,剛剛好用了f這個名字,這樣就導緻原來那個減法的方法“故障”

在實際程式設計中,我們常常會通過重寫父類的方法來完成新的功能,這樣寫起來雖然簡單,但是整個繼承體系的可複用性會比較差,特别是運用多态比較頻繁時,程式運作出錯的幾率非常大。如果非要重寫父類的方法,比較通用的做法是:原來的父類和子類都繼承一個更通俗的基類,原有的繼承關系去掉,采用依賴、聚合,組合等關系代替。

裡氏替換原則通俗的來講就是:子類可以擴充父類的功能,但不能改變父類原有的功能。它包含以下4層含義:

  • 子類可以實作父類的抽象方法,但不能覆寫父類的非抽象方法。
  • 子類中可以增加自己特有的方法。
  • 當子類的方法重載父類的方法時,方法的前置條件(即方法的形參)要比父類方法的輸入參數更寬松。
  • 當子類的方法實作父類的抽象方法時,方法的後置條件(即方法的傳回值)要比父類更嚴格。

Pre-condition:

  • 每個方法調用之前,該方法應該校驗傳入參數的正确性,隻有正确才能執行該方法,否則認為調用方違反契約,不予執行。這稱為前置條件(Pre-condition)。

Post-Condition:

  • 一旦通過前置條件的校驗,方法必須執行,并且必須確定執行結果符合契約,這稱之為後置條件(Post-condition)。

Invariant:

  • 對象本身有一套對自身狀态進行校驗的檢查條件,以確定該對象的本質不發生改變,這稱之為不變式(Invariant)。

以上是單個對象的限制條件。為了滿足LSP,當存在繼承關系時,子類中方法的前置條件必須與超類中被覆寫的方法的前置條件相同或者更寬松;而子類中方法的後置條件必須與超類中被覆寫的方法的後置條件相同或者更為嚴格

繼承并且覆寫超類方法的時候,子類中的方法的可見性必須等于或者大于超類中的方法的可見性,子類中的方法所抛出的受檢異常隻能是超類中對應方法所抛出的受檢異常的子類。

從Java5開始,子類中的方法的傳回值也可以是對應的超類方法的傳回值的子類。這叫做"協變"(Covariant)

繼續閱讀