1. 程式人生 > 其它 >《設計模式》——里氏替換原則

《設計模式》——里氏替換原則

技術標籤:設計模式學習總結

1、什麼是里氏替換原則

Liskov於1987年提出了一個關於繼承的原則“Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.”——“繼承必須確保父類所擁有的性質在子類中仍然成立。”也就是說,當一個子類的例項應該能夠替換任何其父類的例項時,它們之間才具有is-A關係。該原則稱為Liskov Substitution Principle——里氏替換原則。

2、程式碼示例

正方形不是長方形:

在我們的認知範圍內,長方形的長不等於寬,正方形是長等於寬的長方形,正方形是一種特殊的長方形。但在實際編碼過程中遇到的情況是怎麼樣的呢,通過程式碼分析:

package cn.com.design.model.test.liskvorole.negative;

/**
 * 專案名稱:OpenCloseRole
 * 類 名 稱:Test
 * 類 描 述:TODO
 * 建立時間:2021/1/18 下午8:32
 * 創 建 人:wteng
 */

/**
 * 在我們的認知範圍內,一致認為正方形就是一個特殊的長方形
 * 我們也知道,兩個類只要有"is a"關係,兩個類就可以發生繼承關係。
 * 里氏替換原則:任何使用父類的地方,都能被透明的替換成子類。替換成子類後,程式行為不會發生問題。
 * 在里氏替換原則的指導方針下,可得出:僅僅依據兩個類之間有沒有"is a"的關係,來判斷兩個類能不能發生繼承關係,是不夠的,
 *
 * 以下是反例,正方形不能替換長方形,替換後測試失敗
 */
class Rect { private int width; private int height; public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } public void setHeight(int height) { this
.height = height; } public int area() { return getHeight()*getWidth(); } } class Squaer extends Rect{ private int sideLength; @Override public void setWidth(int width) { this.sideLength = width; } @Override public void setHeight(int height) { this.sideLength = height; } @Override public int getWidth() { return sideLength; } @Override public int getHeight() { return sideLength; } } class Foo { public void testArea(Rect rect) { rect.setWidth(10); rect.setHeight(15); if ((rect.area() == 150)) { System.out.println("測試通過"); } else { System.out.println("測試失敗"); } } } public class Test { public static void main(String[] args) { Foo foo = new Foo(); // Rect rect = new Rect(); Rect squer = new Squaer(); foo.testArea(squer); } }

但是執行結果為測試失敗,說明正方形是特殊的長方形違背了里氏替換原則,為了解決上邊問題,僅僅斷絕square和Rect的繼承關係即可
除此之外,類似經典的案例還有鴕鳥飛鳥。

3.總結

所謂物件是一組狀態和一系列行為的組合。狀態是物件的內在特性,行為是物件的外在特性。LSP所表述的就是在同一個繼承體系中的物件應該有共同的行為特徵。我們在設計物件時是按照行為進行分類的,只有行為一致的物件才能抽象出一個類來。設定長方形的長度的時候,它的寬度保持不變,設定寬度的時候,長度保持不變。正方形的行為:設定正方形的長度的時候,寬度隨之改變;設定寬度的時候,長度隨之改變。所以,如果我們把這種行為加到基類長方形的時候,就導致了正方形無法繼承這種行為。我們“強行”把正方形從長方形繼承過來,就造成無法達到預期的結果。鴕鳥非鳥,能飛是鳥的特性,但鴕鳥是不能飛的,我們強行將其歸為鳥類,最終導致程式碼出錯。

所有子類的行為功能必須和其父類持一致,如果子類達不到這一點,那麼必然違反里氏替換原則。在實際的開發過程中,不正確的派生關係是非常有害的。伴隨著軟體開發規模的擴大,參與的開發人員也越來越多,每個人都在使用別人提供的元件,也會為別人提供元件。最終,所有人的開發的元件經過層層包裝和不斷組合,被整合為一個完整的系統。每個開發人員在使用別人的元件時,只需知道元件的對外裸露的介面,那就是它全部行為的集合,至於內部到底是怎麼實現的,無法知道,也無須知道。所以,對於使用者而言,它只能通過介面實現自己的預期,如果元件介面提供的行為與使用者的預期不符,錯誤便產生了。里氏替換原則就是在設計時避免出現派生類與基類不一致的行為。