里氏替换原则的理解
3 min read

里氏替换原则的理解

里氏替换原则的理解以及在 Go 中的运用。
里氏替换原则的理解
Photo by Brett Jordan / Unsplash

LSP

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

里氏代换原则的严格表述如下:如果对每一个类型为 S 的对象 o1,都有类型为 T 的对象 o2,使得以 T 定义的所有程序 P 在所有的对象 o1 代换 o2 时,程序 P 的行为没有变化,那么类型 S 是类型 T 的子类型。

LSP 定义拓展

一个软件实体如果适用一个父类的话,那么就一定适用其子类,所有引用父类的地方必须能够透明地使用其子类的对象,子类对象能够替换父类对象,而程序逻辑不变。里氏代换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。例如:我喜欢动物,那我一定喜欢狗,因为狗是动物的子类;但是我喜欢狗,不能据此断定我喜欢动物,因为我并不喜欢老鼠,虽然它也是动物。

LSP 引申意义

子类可以扩展父类的功能,但是不能修改父类的功能。

LSP 理解

里氏替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

根据上述理解,对里氏替换原则的定义可以总结如下:

  • 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
  • 子类中可以增加自己特有的方法
  • 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入参数)要比父类的方法更宽松
  • 当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的的输出/返回值)要比父类的方法更严格或相等

如果程序违背了里氏替换原则,则继承类的对象在父类出现的地方会出现运行错误。这时其修正方法是:取消原来的继承关系,重新设计它们之间的关系。

Golang Demo

// 四边形类定义
package liskovsubstitution

type QuardRangle interface {
    Width() int
    Length() int
}
// 长方形实现
package liskovsubstitution

type Rectangle struct {
    length int
    width  int
}

func (r *Rectangle) SetWidth(width int) {
    r.width = width
}

func (r Rectangle) SetLength(length int) {
    r.length = length
}

func (r Rectangle) Width() int {
    return r.width
}

func (r Rectangle) Length() int {
    return r.length
}
// 正方形实现
package liskovsubstitution

type Square struct {
    sideLength int
}

func NewSquare(sideLength int) *Square {
    return &Square{sideLength: sideLength}
}

func (s *Square) SetSideLength(sideLength int) {
    s.sideLength = sideLength
}

func (s Square) Width() int {
    return s.sideLength
}

func (s Square) Length() int {
    return s.sideLength
}
// 测试里氏替换
package liskovsubstitution

import (
    "fmt"
    "testing"
)

func Test(t *testing.T) {
    square := NewSquare(10)

    //resize(square)
}
func resize(rectangle Rectangle) {
    for rectangle.Width() <= rectangle.Length() {
        rectangle.SetWidth(rectangle.Width() + 1)
        fmt.Printf("width:%d,length:%d", rectangle.Width(), rectangle.Length())
    }
}

References

  1. golang设计模式系列(三)面向对象设计原则-里氏替换原则
  2. 里氏代换原则(LSP)

Public discussion

足迹