里斯替换原则 (设计模式)

定义

If S is a subtype of T, then objects of type T may be replaced with objects of type S, without breaking the program。

Functions that use pointers of references to base classes must be able to use objects of derived classes without knowing it。

子类能够替换程序中的父类出现的任何地方,并且保证原来的逻辑行为不发生变化。 我们可能会有这样的疑问,这个厕不是面向对象的多态的特性吗? 多态和里氏替换原则是一回事吗? 

看下边的例子 :

// 改造前:
public class SecurityTransporter extends Transporter {
//... 省略其他代码..
@Override
public Response sendRequest(Request request) {
if (StringUtils.isNotBlank(appId) && StringUtils.isNotBlank(appToken)) {
request.addPayload("app-id", appId);
request.addPayload("app-token", appToken);
}
return super.sendRequest(request);
}
}
// 改造后:
public class SecurityTransporter extends Transporter {
//... 省略其他代码..
@Override
public Response sendRequest(Request request) {
if (StringUtils.isBlank(appId) || StringUtils.isBlank(appToken)) {
throw new NoAuthorizationRuntimeException(...);
}
request.addPayload("app-id", appId);
request.addPayload("app-token", appToken);
return super.sendRequest(request);
}
}

  

这个代码子类会抛出异常,这个例子就不符合里氏替换原则。 

从定义描述和代码的实现上来看,多态和里氏替换原则非常的像,但是他们关注的角度是不一样的。 多态是面向对象编程的一大特性,也是面向对象的一种语法。它是一种设计思路。 里斯替换原则 是一种设计原则。是用来知道继承关系中的子类该如何设计。 子类的设计哟啊保证在替换父类的时候,不改变原有的逻辑,不破还原有的程序的正确性。 

哪些代码明显违背了 LSP?

看起来比较抽象,我来进一步解读一下。子类在设计的时候,要遵守父类的行为约定(或者 叫协议)。父类定义了函数的行为约定,那子类可以改变函数的内部实现逻辑,但不能改变 函数原有的行为约定。这里的行为约定包括:函数声明要实现的功能;对输入、输出、异常 的约定;甚至包括注释中所罗列的任何特殊说明。实际上,定义中父类和子类之间的关系, 也可以替换成接口和实现类之间的关系。

1. 子类违背父类生命要实现的功能

如果父类中的sort() 方法是按照id ,排序的,子类中的sort() 方法是按照日期来排序的。这个就是违背了里氏替换原则

2. 子类违背父类对输入输出,异常的约定

在父类中约定,运行出错时返回null ,获取数据为空返回空集合,而子类重载后,实现边了,运行异常时抛出execption 。 获取不到数据返回nul,那子类的设计就违背了这个原则。 

在父类中,约定输入的数据可以是任意整数,但是在子类只允许输入整数,子类的输入比父类更严格了,那么子类的设计违背了这个原则。 

3. 子类违背父类注解中所罗列的任何特殊说明

父类中定义的 withdraw() 提现函数的注释是这么写的:“用户的提现金额不得超过账户余 额……”,而子类重写 withdraw() 函数之后,针对 VIP 账号实现了透支提现的功能,也就 是提现金额可以大于账户余额,那这个子类的设计也是不符合里式替换原则的。

 

posted @ 2024-02-27 13:37  dousil  阅读(32)  评论(0)    收藏  举报