Liskov Substitution Principle - 里氏代换原则

里氏代换原则 —— LSP

任何基类可以出现的地方,子类一定可以出现!

  里氏替换原则,OCP作为OO的高层原则,主张使用“抽象(Abstraction)”和“多态(Polymorphism)”将设计中的静态结构改为动态结构,维持设计的封闭性。“抽象”是语言提供的功能。“多态”由继承语义实现。

里氏替换原则包含以下4层含义:

  • 子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。
  • 子类可以增加自己特有的方法。
  • 当子类覆盖或者实现父类的方法时,方法的前置条件(方法的形象)要比父类的方法输入参数更宽松。
  • 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值) 要比父类的更加严谨。

  LSP 一个软件如果使用的是一个父类的话, 那么一定适用于其子类, 而察觉不出父类对象和子类对象的区别。 也即是说,在软件里面, 把父类替换成它的子类, 程序的行为不会有变化, 简单地说, 子类型必须能够替换掉它们的父类型。 举个例子:企鹅和鸟的关系,企鹅在生物学上属于鸟类,但在面对对象设计中 企鹅并不能以父类(鸟)的身份出现 ,因为企鹅并不具备鸟飞行的行为! 如果有两个具体的类:A,B 之间的关系违反了LSP的设计,那么根据具体的情况可以在下面的两种重构方案中选择:

  • 创建一个新的超类C(抽象类),将A,B的共同行为转移到C中来解决问题。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    class Dad {
    void 菜() {
    System.out.println("红烧排骨");
    }
    }
    class Son extends Dad {
    void 菜(){
    System.out.println("红烧鱼");
    }
    void 西点(){
    System.out.println("蛋糕");
    }
    }
    Son s = new Son();
    s.菜();

    class Dad extends Mom {
    void 菜() {
    super.家传菜("红烧排骨");
    }
    }
    class Son extends Mom {
    void 菜(){
    super.家传菜("红烧鱼");
    }
    void 西点(){
    System.out.println("蛋糕");
    }
    }
    class Mom {
    void 家传菜(String 菜) {
    System.out.println(菜);
    }
    }

    Son s = new Son();
    s.菜();

    Dad d = new Son();
    d.菜();
    这样的话行为没有改变,行为都是做家传菜 但是做出来的家传菜内容不一样!
  • 以下方式经常被实际运用

    从B到A的继承关系改为委派关系。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    原本继承关系(Inheritance)
    class A {
    void print() {
    System.out.println("A Data");
    }
    }
    class B extends A {
    void print(){
    super.print();
    }
    }
    B b = new B();
    b.print();

    变成委派关系(Delegation)

    class A {
    void print() {
    System.out.println("The Delegate");
    }
    }
    class B {
    A a = new A();
    void print() {
    a.print();
    }
    }
    B b = new B();
    b.print();
分享到