設計原則之里氏替換原則


定義:所有引用基類的地方必須能透明地使用其子類的對象。

問題:有一功能P1,由類A來完成。現在需要將功能P1進行擴展,擴展后的功能為P(P由原有功能P1和新功能P2組成)。
功能P由類A的子類B來完成,子類B在完成新功能P2的同時有可能會導致原有功能P1發生故障。

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

舉個栗子:士兵使用武器進行射擊,包括武器的類別和特點的介紹。

情況一:士兵使用手槍進行射擊,實現結果為:手槍小,便於攜帶;手槍一次只能發射一顆子彈;手槍開始射擊。
1. 新建一個手槍類HandGun,介紹手槍的特點,代碼如下:

2. 新建一個士兵類Soldier,包含手槍的相關方法,代碼如下:

3. 在類LSPFragment中實現士兵用手槍進行射擊,代碼如下:

4. 運行后的結果為:

現在我們需要實現士兵使用步槍進行射擊,於是我們就要新增步槍類RifleGun,並且要去修改士兵類Soldier。

情況二:士兵使用步槍進行射擊,實現結果為:步槍長,不益於攜帶;步槍可以連續射擊;步槍開始射擊。
1.新建一個步槍類RifleGun,介紹步槍的特點,代碼如下:

2. 修改士兵類Soldier,包含手槍和步槍的相關方法,代碼如下:

3. 修改類LSPFragment,分別實現士兵用手槍和步槍進行射擊,代碼如下:

4. 運行后的結果為:



以上的實現方式可以看出,一旦新增功能(武器),就會去修改類Soldier和類LSPFragment,這樣不僅會影響到原有的功能,
擴展起來也十分不方便,假設邏輯比較復雜的話,就需要去修改或重構大量的代碼。

於是就要使用里氏替換原則來解決這個問題,新增一個類BaseGun,包含武器的特點和射擊動作的方法,讓手槍類HandGun和
步槍類RifleGun都繼承於它,分別來實現,代碼如下:

1. 新增類BaseGun,包含武器的特點和射擊動作的方法,代碼如下:

2. 讓手槍類HandGun和步槍類RifleGun都繼承於它,分別來實現以上兩個方法,代碼如下:

3. 修改士兵類Soldier,讓它依賴類BaseGun,代碼如下:


4. 修改類LSPFragment,實現士兵用手槍和步槍進行射擊,代碼如下:

5. 運行后的結果同上。
以上實現方式可以看出,子類(手槍類HandGun和步槍類RifleGun)繼承父類BaseGun,並完全實現父類BaseGun中的抽象方法來實現功能。
其中,士兵類Soldier依賴於抽象類BaseGun來調用相應的方法,在類LSPFragment中可以看出,父類都可以用子類來代替,但不能改變父類
原有的功能,在子類中可以任意擴展功能,這就是所謂的“所有引用基類的地方必須能透明地使用其子類的對象。”
下面,我們再新增一個武器類(機槍類CachineGun),並在各個子類中分別擴展功能,比如增加武器名。代碼如下:

情況三:
1. 士兵使用手槍進行射擊,實現結果為:士兵使用的武器是手槍;手槍小,便於攜帶;手槍一次只能發射一顆子彈;手槍開始射擊。
2. 士兵使用步槍進行射擊,實現結果為:士兵使用的武器是步槍;步槍長,不益於攜帶;步槍可以連續射擊;步槍開始射擊。
3. 士兵使用機槍進行射擊,實現結果為:士兵使用的武器是機槍;機槍長,火力十分凶猛;機槍可以掃射;機槍開始射擊。
代碼如下所示:
1. 新建一個機槍類CachineGun,介紹機槍的特點,增加機槍獨有的方法,代碼如下:

2. 修改手槍類HandGun和步槍類RifleGun,分別增加各自獨有的方法,代碼如下:

3. 此時不需要修改類Solider,只需要在類LSPFragment中增加調用方法,即可實現士兵用手槍、步槍和機槍進行射擊,代碼如下:

4. 運行后的結果為:

以上,使用繼承抽象類來擴展功能,並遵循里氏替換原則,實現起來是不是很方便呀!

原則:子類可以擴展父類的功能,但不能改變父類原有的功能。
父類能出現的地方都可以用子類來代替,而且換成子類也不會出現任何錯誤或異常,而使用者也無需知道是父類還是子類,
但反過來則不成立。總之,就是抽象。

1. 子類必須完全實現父類的抽象方法,但不能覆蓋父類的非抽象方法;
2. 子類中可以增加自己特有的方法;
3. 當子類的方法重載父類的方法時,方法的前置條件(即方法的形參)要比父類方法的輸入參數要更寬松;
4.當子類的方法實現父類的抽象方法時,方法的后置條件(即方法的返回值)要比父類更嚴格。

優點:
1. 提高代碼的重用性,子類擁有父類的方法和屬性;
2. 提高代碼的可擴展性,子類可形似於父類,但異於父類,保留自我的特性;

缺點:
1. 繼承是侵入性的,只要繼承就必須擁有父類的所有方法和屬性,在一定程度上約束了子類,降低了代碼的靈活性;
2. 增加了耦合,當父類的常量、變量或者方法被修改了,需要考慮子類的修改,所以一旦父類有了變動,很可能會造成
非常糟糕的結果,要重構大量的代碼。


免責聲明!

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



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