里氏代换原则由2008年图灵奖得主、美国第一位计算机科学女博士Barbara Liskov教授和卡内基·梅隆大学Jeannette Wing 教授于1994年提出,因此使用的是这位女博士的性命名的一个设计原则。html
里氏替换原则(Liskov Substitution Principle, LSP):全部引用父类的地方必须能使用其子类的对象。ide
从这个概念能够看出这个原则是面向对象多态的一种具体实践。通俗来说 “老爸能干的事情,儿子都能干”, 由于儿子继承了老爸的基因。 反过来说就不对了,时代在变化,新一代虽然继承了老一代的优良传统,可是在时代的影响下,新一代有了一些新的特性,老一代可能就不具有了,好比如今的年轻人会打游戏,可是他爸不必定会。老爸会骑自行车,换成儿子也能骑。spa
一样的里氏代换原则告诉咱们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,若是一个软件实体使用的是一个子类对象的话,那么它不必定可以使用父类对象。设计
咱们定义一个父类叫Animal, 其包含一个方法叫Say以下:code
public class Animal { private readonly string _sayContent; public Animal(string sayContent) { _sayContent = sayContent; } public virtual void Say() { Console.WriteLine($"Animal Say:{_sayContent}"); } }
再定义一个子类Pig 集成自Animal,并覆盖父类中的Say 方法以下:htm
public class Pig:Animal { private readonly string _sayContent; public Pig(string sayContent) : base(sayContent) { _sayContent = sayContent; } public override void Say() { Console.WriteLine($"Pig Say:{_sayContent}"); } }
如今咱们在调用方建立一个Animal的对象并调用Say方法:对象
Animal animal = new Animal("This is a parent class."); animal.Say();
输出结果:blog
Animal Say:This is a parent class.继承
下来咱们建立一个Pig对象赋给animal 对象并调用Say方法:接口
static void Main(string[] args) { Animal animal = new Animal("This is a parent class."); animal.Say(); animal = new Pig("This is a sub class."); animal.Say(); Console.ReadKey(); }
输出:
能够看出将子类的对象赋给父类的对象,而且获得了咱们指望的结果。
里氏替换原则是实现开闭原则的重要方式之一(其实其它原则都是实现开闭原则OCP重要方式之一,上一篇【面向对象设计原则】之开闭原则(OCP) 有说起),因为使用父类对象的地方均可以使用子类对象,所以在程序中尽可能使用父类类型来对对象进行定义,而在运行时再肯定其子类类型,用子类对象来替换父类对象。一般咱们会使用接口或者抽象方法定义基类,而后子类中实现父类的方法,并在运行时经过各类手段进行类型选择调用(好比反射)。
在使用里氏替换原则时须要注意以下几个问题:
(1)子类的全部方法必须在父类中声明,或子类必须实现父类中声明的全部方法。根据里氏替换原则,为了保证系统的扩展性,在程序中一般使用父类来进行定义,若是一个方法只存在子类中,在父类中不提供相应的声明,则没法在以父类定义的对象中使用该方法。
(2) 咱们在运用里氏替换原则时,尽可能把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实如今父类中声明的方法,运行时,子类实例替换父类实例,咱们能够很方便地扩展系统的功能,同时无须修改原有子类的代码,增长新的功能能够经过增长一个新的子类来实现。里氏替换原则是开闭原则的具体实现手段之一。这也就是咱们应该更多的依赖抽象,尽可能少的依赖实现细节, 其实就是咱们下一篇要讲的依赖倒置原则(DIP)。