1. 程式人生 > >設計模式六大原則之裏氏替換原則

設計模式六大原則之裏氏替換原則

number -h ole 擁有 method about rect sse 程序

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 正方形不是長方形

在我們的認知範圍內,長方形的長不等於寬,正方形是長等於寬的長方形,正方形是一種特殊的長方形。但在實際編碼過程中遇到的情況是怎麽樣的呢,通過代碼分析:
 1
public 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 public
override 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 Height
25 { 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所表述的就是在同一個繼承體系中的對象應該有共同的行為特征。我們在設計對象時是按照行為進行分類的,只有行為一致的對象才能抽象出一個類來。設置長方形的長度的時候,它的寬度保持不變,設置寬度的時候,長度保持不變。正方形的行為:設置正方形的長度的時候,寬度隨之改變;設置寬度的時候,長度隨之改變。所以,如果我們把這種行為加到基類長方形的時候,就導致了正方形無法繼承這種行為。我們“強行”把正方形從長方形繼承過來,就造成無法達到預期的結果。鴕鳥非鳥,能飛是鳥的特性,但鴕鳥是不能飛的,我們強行將其歸為鳥類,最終導致代碼出錯。

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

設計模式六大原則之裏氏替換原則