哪些代码毁坏了 LSP?
实际上,里式替换准则还有另外一个更加能落地、更有指导意义的形容,那就是
Design By Contract
,中文翻译就是依照协定来设计
。
子类在设计的时候,要恪守父类的行为约定(或者叫协定)。父类定义了函数的行为约定,那子类能够扭转函数的外部实现逻辑,但不能扭转函数原有的行为约定。这里的行为约定包含:
- 函数申明要实现的性能;
- 对输出、输入、异样的约定;
- 正文中所列举的任何非凡阐明。
实际上,定义中父类和子类之间的关系,也能够替换成接口和实现类之间的关系。
为了更好地了解这句话,我举几个违反里式替换准则的例子来解释一下。
子类违反父类申明要实现的性能
父类
中提供的 sortOrdersByAmount ()
订单排序函数,是依照金额从小到大来给订单排序的,而 子类
重写这个 sortOrdersByAmount ()
订单排序函数之后,是依照创立日期来给订单排序的。那子类的设计就违反里式替换准则。
子类违反父类对输出、输入、异样的约定
- 在父类中,某个函数约定:运行出错的时候返回
null
,获取数据为空的时候返回空集合(empty collection)。 - 子类重载函数之后,实现变了,运行出错返回异样(exception),获取不到数据返回 null。那子类的设计就违反里式替换准则。
- 在父类中,某个函数约定,输出数据能够是任意整数,但子类实现的时候,只容许输出数据是正整数,正数就抛出,也就是说,子类对输出的数据的校验比父类更加严格,那子类的设计就违反了里式替换准则。
在父类中,某个函数约定,只会抛出 ArgumentNullException 异样,那子类的设计实现中只容许抛出 ArgumentNullException 异样,任何其余异样的抛出,都会导致子类违反里式替换准则。
子类违反父类正文中所列举的任何非凡阐明
- 父类中定义的 withdraw () 提现函数的正文是这么写的:“用户的提现金额不得超过账户余额……”
- 而子类重写 withdraw () 函数之后,针对 VIP 账号实现了透支提现的性能,也就是提现金额能够大于账户余额,那这个子类的设计也是不合乎里式替换准则的。
以上便是三种典型的违反里式替换准则的状况。
除此之外,判断子类的设计实现是否违反里式替换准则,还有一个小窍门,那就是拿父类的单元测试去验证子类的代码。如果某些单元测试运行失败,就有可能阐明,子类的设计实现没有齐全地恪守父类的约定,子类有可能违反了里式替换准则。
实际上,你有没有发现,里式替换这个准则是十分宽松的。个别状况下,咱们写的代码都不怎么会违反它。所以,只有你能看懂我明天讲的这些,这个准则就不难把握,也不难利用。
尽管从定义形容和代码实现上来看,多态和里式替换有点相似,但它们关注的角度是不一样的。
- 多态是面向对象编程的一大个性,也是面向对象编程语言的一种语法。它是一种代码实现的思路。
- 里式替换是一种设计准则,是用来领导继承关系中子类该如何设计的,子类的设计要保障在替换父类的时候,不扭转原有程序的逻辑以及不毁坏原有程序的正确性。
更多原创浏览:https://javawu.com