里氏代换学习

概念:
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。
里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。
LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,
基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。
而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
我的理解:
关于这个原则,我觉得这是一个非常抽象的概念。
父类出现的地方,一般子类出现是没问题的,这是面向对象的保证。
那么这个原则限制的是什么呢?限制的是需求层的行为。
对象替换了,得保证行为正常?
这咋保证,能保证吗?
一般网上会举这么一个反例?
企鹅属于鸟类,但是企鹅不能飞?
比如有这么一个调用

移动高度(鸟 麻雀){
麻雀:飞离地面50米。
}
移动高度(鸟 企鹅){
企鹅:飞离地面0米。
}
那么这我认为是否符里氏替换取决于,在需求的层面上能不能接受,飞离地面一米。
入这个飞的动作针对动物的关系系统中,是有飞这个动作,不能飞的动物就抓不到它了。
那么这个就不符和里氏替换的原则了。
而如果飞这个动作表示翅膀的运动,那么企鹅虽然飞了0米高,那么这也符和里氏替换的原则。
那么在回到对鸟的这个动作的设计之初。
如果对鸟这个概念的设计,包含了飞这个属性。那么将企鹅划入鸟的类别中是没有问题的。
但是如果对鸟的这个概念的设计,不包含飞,那么就没有违背里氏替换法则的问题了。
所以这个只是对概念设计的一个最基本的约束,如果我们对鸟和企鹅的共性最初没有分析清楚。
谈后来是否破坏里氏替换原则没有意义。
如果后来需求真的有变化了了,和最初对鸟和企鹅的设计期待有差别了。
那么是对鸟和企鹅的最初设计概念发生变化了,不能说对象的设计违背了里氏替换原则。
引用文章:里氏代换原则
当然,概念的东西,经历过多次变更,或多人的维护后,一定已经不那么清晰了。
“里氏替换原则”总有被违背的时候,这时候这么办呢?
那就得具体问题具体分析了。
针对上面的企鹅问题,如果以能否脱了地面为准,那么可以定义为企鹅不能飞。让企鹅的飞抛出异常。
或者其他的手段。
有时候也可以采用重构的手段使之符和里氏替换原则,里氏替换本质上不是一个原则,是bug,不是我们要不要遵守的问题。
而是一旦给原则被破坏我们必须修复的问题。
如果真的有个类不符和里氏替换的原则了,这时候我们最好重新分类,建立起新的继承关系。
或着以依赖,组合的方式来处理 企鹅和鸟的关系。
这是一个我见到的一个挺好且清晰的解决思路:设计模式里氏替换原则继承优缺点子类必须完全实现父类的方法
关于里氏替换的深入分析
一下看到的关于该问题的一些总结性的思考,觉得总计的挺好。
说的本质是概念的宽窄的问题,这里提出了一个用契约的方式保证里氏替换不被破坏的方法。
LSP并没有提供解决这个问题的方案,而只是提出了这么一个问题。
于是,工程师们开始关注如何确保对象的行为。1988年,B. Meyer提出了Design by Contract(契约式设计)理论。DbC从形式化方法中借鉴了一套确保对象行为和自身状态的方法,其基本概念很简单:
Pre-condition:
每个方法调用之前,该方法应该校验传入参数的正确性,只有正确才能执行该方法,否则认为调用方违反契约,不予执行。这称为前置条件(Pre-condition)。
Post-Condition:
一旦通过前置条件的校验,方法必须执行,并且必须确保执行结果符合契约,这称之为后置条件(Post-condition)。
Invariant:
对象本身有一套对自身状态进行校验的检查条件,以确保该对象的本质不发生改变,这称之为不变式(Invariant)。
以上是单个对象的约束条件。为了满足LSP,当存在继承关系时,子类中方法的前置条件必须与超类中被覆盖的方法的前置条件相同或者更宽松;而子类中方法的后置条件必须与超类中被覆盖的方法的后置条件相同或者更为严格
引用来源:里氏替换原则

你可能感兴趣的:(设计)