里氏代换原则由2008年图灵奖得主、美国第一位计算机科学女博士Barbara Liskov教授和卡内基·梅隆大学Jeannette Wing 教授于1994年提出,所以使用的是这位女博士的性命名的一个设计原则。

里氏替换原则(Liskov Substitution Principle, LSP):所有引用父类的地方必须能使用其子类的对象。

从这个概念可以看出这个原则是面向对象多态的一种具体实践。通俗来讲 “老爸能干的事情,儿子都能干”, 因为儿子继承了老爸的基因。 反过来讲就不对了,时代在变化,新一代虽然继承了老一代的优良传统,但是在时代的影响下,新一代有了一些新的特性,老一代可能就不具备了,比如现在的年轻人会打游戏,但是他爸不一定会。老爸会骑自行车,换成儿子也能骑。

同样的里氏代换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用父类对象。

我们定义一个父类叫Animal, 其包含一个方法叫Say如下:

    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 方法如下:

    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();

输出结果:

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)。

【面向对象设计原则】之里氏替换原则(LSP)的更多相关文章

  1. C# 实例解释面向对象编程中的里氏替换原则

    在面向对象编程中,SOLID 是五个设计原则的首字母缩写,旨在使软件设计更易于理解.灵活和可维护.这些原则是由美国软件工程师和讲师罗伯特·C·马丁(Robert Cecil Martin)提出的许多原 ...

  2. IOS设计模式的六大设计原则之里氏替换原则(LSP,Liskov Substitution Principle)

    定义 里氏替换原则的定义有两种,据说是由麻省理工的一位姓里的女士所提出,因此以其名进行命名. 定义1:如果对一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1所定义的程序P中在o1全都替换 ...

  3. 深入理解JavaScript系列(8):S.O.L.I.D五大原则之里氏替换原则LSP

    前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第3篇,里氏替换原则LSP(The Liskov Substitution Principle ). 英文原文:http ...

  4. Java设计原则之里氏替换原则

    里氏代换原则由2008年图灵奖得主.美国第一位计算机科学女博士Barbara Liskov教授和卡内基·梅隆大学Jeannette Wing教授于1994年提出.其严格表述如下:如果对每一个类型为S的 ...

  5. 设计模式学习--面向对象的5条设计原则之Liskov替换原则--LSP

    一.LSP简介(LSP--Liskov Substitution Principle): 定义:如果对于类型S的每一个对象o1,都有一个类型T的对象o2,使对于任意用类型T定义的程序P,将o2替换为o ...

  6. S.O.L.I.D: PHP 面向对象设计的五个基准原则

    S.O.L.I.D 是首个 5 个面向对象设计 (OOD) 准则的首字母缩写,这些准则是由 Robert C. Martin 提出的,他更为人所熟知的名字是 Uncle Bob. 这些准则使得开发出易 ...

  7. [OOD]违反里氏替换原则的解决方案

    关于OOD中的里氏替换原则,大家耳熟能祥了,不再展开,可以参考设计模式的六大设计原则之里氏替换原则.这里尝试讨论常常违反的两种形式和解决方案. 违反里氏替换原则的根源是对子类及父类关系不明确.我们在设 ...

  8. 【C#设计模式】里氏替换原则

    今天,我们再来学习 SOLID 中的"L"对应的原则:里式替换原则. 里氏替换原则 里氏替换原则(Liskov Substitution Principle):派生类(子类)对象能 ...

  9. 第2章 面向对象的设计原则(SOLID):2_里氏替换原则(LSP)

    2. 里氏替换原则(Liskov Substitution Principle,LSP) 2.1 定义 (1)所有使用基类的地方必须能透明地使用子类替换,而程序的行为没有任何变化(不会产生运行结果错误 ...

随机推荐

  1. C# 遍历泛型集合

    /// <summary> /// 遍历泛型 /// </summary> /// <typeparam name="T"></typep ...

  2. 连接greenvpn 公司内网连接

    查看计算机路由表 Route print 跟踪路由表 Tracert ip or dnsname 本次需求,连接greenvpn之后,公司10.130.41.** 网段的机器无法使用.本机ip 10. ...

  3. 关于php 高并发解决的一点思路

    涉及抢购.秒杀.抽奖.抢票等活动时,为了避免超卖,那么库存数量是有限的,但是如果同时下单人数超过了库存数量,就会导致商品超卖问题.那么我们怎么来解决这个问题呢,我的思路如下(伪代码): sql1:查询 ...

  4. Java --- JSP2新特性

    自从03年发布了jsp2.0之后,新增了一些额外的特性,这些特性使得动态网页设计变得更加容易.jsp2.0以后的版本统称jsp2.主要的新增特性有如下几个: 直接配置jsp属性 表达式语言(EL) 标 ...

  5. RHEL7对比RHEL6的主要变化

    RHEL7和RHEL6的主要变化 RHEL7和RHEL6的主要变化   RHEL7 RHEL6 文件系统 XFS EXT4 内核版本 3.10.x-x系列 2.6.x-x系列 内核名称 Maipo S ...

  6. Mvc自定义验证

    假设我们书店需要录入一本书,为了简单的体现我们的自定义验证,我们的实体定义的非常简单,就两个属性,一个名称Name,一个出版社Publisher. public class BookInfo { pu ...

  7. Linux--管道pipe

    管道是一种最基本的IPC机制,由pipe函数创建:#include <unistd.h>
int pipe(int filedes[2]); 调用pipe函数时在内核中开辟一块缓冲区(称为 ...

  8. pwm最后的解释

    之前学东西总是模模糊糊,前几天看了pwm,虽然知道怎么配置,但是如果让我自己去写一个pwm的程序,我却不知如何下手. 不知道如何配置他的频率和占空比.今天痛定思痛,决定彻底搞懂pwm. 百度给 的答案 ...

  9. STM32F4XX与STM32F0XX编程差别

    //普通管脚初始化 /*****************************************************************************STM32F0***** ...

  10. Linux 基础(5)

    Linux 基础 (五) 一.shell相关知识 shell一般代表两个层面的意思,一个是命令解释器,比如BASH,另外一个就是shell脚本.通过解释器的角度来理解shel 命令分为: ==> ...