• 设计模式六大原则之里氏替换原则


    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所表述的就是在同一个继承体系中的对象应该有共同的行为特征。我们在设计对象时是按照行为进行分类的,只有行为一致的对象才能抽象出一个类来。设置长方形的长度的时候,它的宽度保持不变,设置宽度的时候,长度保持不变。正方形的行为:设置正方形的长度的时候,宽度随之改变;设置宽度的时候,长度随之改变。所以,如果我们把这种行为加到基类长方形的时候,就导致了正方形无法继承这种行为。我们“强行”把正方形从长方形继承过来,就造成无法达到预期的结果。鸵鸟非鸟,能飞是鸟的特性,但鸵鸟是不能飞的,我们强行将其归为鸟类,最终导致代码出错。

    所有子类的行为功能必须和其父类持一致,如果子类达不到这一点,那么必然违反里氏替换原则。在实际的开发过程中,不正确的派生关系是非常有害的。伴随着软件开发规模的扩大,参与的开发人员也越来越多,每个人都在使用别人提供的组件,也会为别人提供组件。最终,所有人的开发的组件经过层层包装和不断组合,被集成为一个完整的系统。每个开发人员在使用别人的组件时,只需知道组件的对外裸露的接口,那就是它全部行为的集合,至于内部到底是怎么实现的,无法知道,也无须知道。所以,对于使用者而言,它只能通过接口实现自己的预期,如果组件接口提供的行为与使用者的预期不符,错误便产生了。里氏替换原则就是在设计时避免出现派生类与基类不一致的行为。

  • 相关阅读:
    JS 弹窗“是否删除”
    input file 保存图片
    Form之action提交不刷新不跳转
    checkbox实现单选
    最近的项目系之2——core3.0整合Autofac
    最近的项目系列1——core整合SPA
    最近的项目之开篇
    短信验证码“最佳实践”
    Asp.net core使用MediatR进程内发布/订阅
    记一次带层级结构列表数据计算性能优化
  • 原文地址:https://www.cnblogs.com/smilejeffery/p/6813468.html
Copyright © 2020-2023  润新知