《架构整洁之道》第 9 章 LSP:里氏替换原则

2023-05-25 13:50:55 浏览数 (3)

均为原创,读架构整洁之道的笔记。

里氏替换原则:(LSP :Liskov Substitution Principle)。

使用一个父类对象,替换成该父类对象的子类对象后,该程序不会发生异常。(该书指的为接口,而没有提到继承关系)

或者说,调用一个Interface,切换成直接调用该接口的实现对象后,该程序不会发生异常。(该书中的表述为接口)

继承的使用指导

场景

Billing调用License,获取授权费用。License接口有两个实现类。这两个实现类中计算授权费用的规则不一样,但是业务是一样的。按照业务切换实现类,业务是可以正常进行的,不会破坏程序的正确性,不会发生异常。最关键的是这两个实现类,可以直接替换掉License接口。这样就是符合LSP原则的。

正方形/长方形问题

以下设计中,正方形/长方形问题,是一个著名的违反LSP的设计案例。

可以看到,User调用Rectangle为获得长方形的面积,可设置宽高。而正方形与长方形的设置宽高的逻辑并不一致,所以正方形错误的继承了Rectangle。如果使用Square,来替换掉Rectangle,就会发现错误。看下面的例子。

代码语言:javascript复制
Rectangle r = ... //当这里是长方形时Rectangle,这个断言自然能通过。但是
  //如果切换成正方形Square时,这个断言是通过不了的。
r.setW(5);
r.setH(2);
assert(r.area()==10);

即可以确认,子类型,并不能完全替代其父类,会发生逻辑上的问题。所以这是一个违反LSP的案例,正方形不该继承于或者说成为长方形的子类型。

要想防范这种LSP的行为,唯一的办法就是在User类中增加用于区分RectangleSquare的检测逻辑,如if语句,但是这就造成了强依赖,使用者强烈依赖被使用者。

LSP 与软件架构

可以看出,上述讲的是类和接口的继承与实现关系。然而随着时间推移,LSP演变成了一种更广泛的,指导接口与其实现方式的设计原则。

违反 LSP 的案例

书中举的是一个出租车调度服务程序,面向多个出租车公司,用户发出请求,由调度服务运算选择出租车公司中的某一辆车,但是其中有一家公司的调用方式,和其他公司不一样,所以很难做切换和统一。

面对系统要向多个第三方提供服务,接口设计应当在调用方式和存储上,统一格式,并能严格区分和识别流量。使其在切换调用第三方时,代码不用做改动。这里将第三方看作是可替换的组件。

本章小结

LSP可以且应该被用于软件架构层面,因为一旦违反了可替换性,该系统就不得不为此增添复杂的应对机制。

1 人点赞