設計模式六大原則之裏氏替換原則
1 裏氏替換原則定義
Liskov於1987年提出了一個關於繼承的原則“Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.”——“繼承必須確保父類所擁有的性質在子類中仍然成立。”也就是說,當一個子類的實例應該能夠替換任何其父類的實例時,它們之間才具有is-A關系。該原則稱為Liskov Substitution Principle——裏氏替換原則。
2 經典案例
2.1 正方形不是長方形
在我們的認知範圍內,長方形的長不等於寬,正方形是長等於寬的長方形,正方形是一種特殊的長方形。但在實際編碼過程中遇到的情況是怎麽樣的呢,通過代碼分析:1public class Rectangle 2 { 3 public virtual int Width { get; set; } 4 public virtual int Height{ get; set; } 5 public int Area() 6 { 7 return Width * Height; 8 } 9 } 10 public class Square : Rectangle 11 { 12 publicoverride int Width 13 { 14 get 15 { 16 return base.Width; 17 } 18 set 19 { 20 base.Width = value; 21 base.Height = value; 22 } 23 } 24 public override int Height25 { 26 get 27 { 28 return base.Height; 29 } 30 set 31 { 32 base.Height = value; 33 base.Width = value; 34 } 35 } 36 }
Square繼承Rectangle。按照裏氏替換原則原則,只要是父類出現的地方都可以用子類進行替換。
1 public static void TestMethod(Rectangle rec) 2 { 3 rec.Width = 10; 4 rec.Height = 15; 5 var area = rec.Area(); 6 Assert.AreEqual(150, area); 7 }
那麽該方法中的Rectangle rec是可以替換成Square的。我們試試:
1 static void Main(string[] args) 2 { 3 Rectangle r = new Rectangle(); 4 TestMethod(r); 5 Square s = new Square(); 6 TestMethod(s); 7 }
運行結果如下:
說明正方形是特殊的長方形違背了裏氏替換原則。
2.2 鴕鳥非鳥
根據生物學的定義鴕鳥實實在在是鳥類,有羽毛幾乎覆蓋全身的卵生脊椎動物,溫血卵生,用肺呼吸,幾乎全身有羽毛,後肢能行走,前肢變為翅,大多數能飛。定義一個鳥類,定義一個繼承鳥類的鴕鳥類。
1 public class Bird 2 { 3 public virtual int FlySpeed{get;set;} 4 public void Fly() { } 5 } 6 public class Ostrich : Bird 7 { 8 public override int FlySpeed 9 { 10 get 11 { 12 return base.FlySpeed; 13 } 14 set 15 { 16 base.FlySpeed = 0;//不會飛 飛不起來 17 } 18 } 19 }
鴕鳥不會飛啊,所以速度為0.
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Ostrich os = new Ostrich(); 6 var time = CalculationFlyTime(os); 7 Console.ReadKey(); 8 } 9 10 public static double CalculationFlyTime(Bird bird) 11 { 12 return 100 / bird.FlySpeed; 13 } 14 15 }
運行程序報錯:
3.總結
所謂對象是一組狀態和一系列行為的組合。狀態是對象的內在特性,行為是對象的外在特性。LSP所表述的就是在同一個繼承體系中的對象應該有共同的行為特征。我們在設計對象時是按照行為進行分類的,只有行為一致的對象才能抽象出一個類來。
設置長方形的長度的時候,它的寬度保持不變,設置寬度的時候,長度保持不變。正方形的行為:設置正方形的長度的時候,寬度隨之改變;設置寬度的時候,長度隨之改變。所以,如果我們把這種行為加到基類長方形的時候,就導致了正方形無法繼承這種行為。我們“強行”把正方形從長方形繼承過來,就造成無法達到預期的結果。鴕鳥非鳥,能飛是鳥的特性,但鴕鳥是不能飛的,我們強行將其歸為鳥類,最終導致代碼出錯。
所有子類的行為功能必須和其父類持一致,如果子類達不到這一點,那麽必然違反裏氏替換原則。在實際的開發過程中,不正確的派生關系是非常有害的。伴隨著軟件開發規模的擴大,參與的開發人員也越來越多,每個人都在使用別人提供的組件,也會為別人提供組件。最終,所有人的開發的組件經過層層包裝和不斷組合,被集成為一個完整的系統。每個開發人員在使用別人的組件時,只需知道組件的對外裸露的接口,那就是它全部行為的集合,至於內部到底是怎麽實現的,無法知道,也無須知道。所以,對於使用者而言,它只能通過接口實現自己的預期,如果組件接口提供的行為與使用者的預期不符,錯誤便產生了。裏氏替換原則就是在設計時避免出現派生類與基類不一致的行為。
設計模式六大原則之裏氏替換原則