面向对象设计原则三:里氏替换原则(LSP)

里氏替换原则(LSP)
定义:在任何父类出现的地方都可以用它的子类类替换,且不影响功能。
解释说明:
其实LSP是对开闭原则的一个扩展,在OO思想中,我们知道对象是由一系列的状态和行为组成的,里氏替换原则说的就是在一个继承体系中,对象应该具有共同的外在特性,使用LSP时,如果想让我们的程序达到一个父类出现的地方都可以用它的子类来替换且不影响功能,那么这个父类也应该尽量声明出子类所需要的一些公共的方法,父类被子类替换之后,会比较顺利,那么为什么说它是对开闭原则的一个扩展呢?因为我们在开闭原则中说尽量使用接口和抽象类,当然这个抽象类和接口也应该尽量定义得完整,这样我们这个接口和抽象类会比较稳定,这样既符合了开闭原则也满足了里氏替换原则。

错误案例1:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace 里氏替换原则
 8 {
 9     /// <summary>
10     /// 鸟类
11     /// </summary>
12     public class Bird
13     {
14         /// <summary>
15         /// 吃的方法
16         /// </summary>
17         public void Eat()
18         { }
19 
20         /// <summary>
21         /// 飞的方法
22         /// </summary>
23         public void Fly()
24         { }
25     }
26 
27     /// <summary>
28     /// 定义一个企鹅类继承鸟类
29     /// </summary>
30     public class Penguin : Bird
31     { 
32     
33     }
34 
35     public class Test
36     {
37         public static void ShowFly(Bird bird)
38         {
39             bird.Fly();
40         }
41 
42         public static void Main()
43         {
44             ShowFly(new Penguin());
45         }
46     }
47 }

解释说明:

在上面的代码中,定义了一个鸟类,企鹅类继承自鸟类。鸟类里面有飞的方法,而企鹅不会飞,所以上面的代码违反了里氏替换原则。

错误案例2:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace 里氏替换原则
 8 {
 9     /// <summary>
10     /// 定义一个父类宠物类
11     /// </summary>
12     public  class Pet
13     {
14     }
15 
16     /// <summary>
17     /// 定义一个企鹅类继承自宠物类
18     /// </summary>
19     public class PenguinDemo : Pet
20     {
21         /// <summary>
22         /// 游泳的方法
23         /// </summary>
24         public void Swiming()
25         {
26             Console.WriteLine("可爱企鹅在游泳");
27         }
28     }
29 
30     /// <summary>
31     /// 定义一个海豚类继承自宠物类
32     /// </summary>
33     public class Dolphin : Pet
34     {
35         /// <summary>
36         /// 玩游戏的方法
37         /// </summary>
38         public void PlayGame()
39         {
40             Console.WriteLine("神奇泡泡打砖块");
41         }
42     }
43 
44     /// <summary>
45     /// 测试类
46     /// </summary>
47     public class Test
48     {
49         public static void ShowPlay(Pet pet)
50         {
51             if (pet is PenguinDemo)
52             {
53                 //类型转换
54                 PenguinDemo pen = (PenguinDemo)pet;
55                 pen.Swiming();
56             }
57             if (pet is Dolphin)
58             {
59                 //类型转换
60                 Dolphin dol = (Dolphin)pet;
61                 dol.PlayGame();
62             }
63         }
64     }
65 }

解释说明:
如果把河豚、企鹅当做宠物,我们可以定义一个宠物类,然后,让这些宠物继承这个类,我们知道每种宠物我们跟他玩耍的方式是不一样的。比如。企鹅有游泳的方法,河豚有游戏的方法,根据这个需求,我们设计一个系统,编写一个宠物类,让企鹅继承这个宠物类,在企鹅类里创建一个游泳的方法,这个方法不能放到宠物类里面,因为并不是所有宠物都会游泳。编写河豚类时,同样让他继承宠物类,在河豚类里面编写一个游戏的方法,这个时候客户端程序在使用宠物类和它的子类的时候,就需要做判断,具体是哪个子类,我们通过宠物类是无法调用具体的方法,要做一个判断和转型,如果在加一个狗类,狗类也会有一个独立的方法,要修改之前的代码(使用宠物类和它的子类的时候,要增加判断是狗类),这很明显不符合开闭原则,也不可能符合里氏替换原则,因为这里面的任何一个宠物都无法替换他的父类,因为他们的行为是不一样的,代码的可维护性和重用性很差!

 代码下载链接:http://files.cnblogs.com/files/dotnet261010/OO%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99.rar

原文地址:https://www.cnblogs.com/dotnet261010/p/7350046.html