【问题标题】:Liskov substitution principle violation里氏替换原则违反
【发布时间】:2017-05-14 01:49:32
【问题描述】:

来自Wikipedia

Liskov 的行为子类型概念定义了 对象的可替代性;也就是说,如果 S 是 T 的子类型,那么 程序中 T 类型的对象可以被 S 类型的对象替换 在不改变该程序的任何理想属性的情况下(例如 正确性)。

假设如下类层次结构:

  1. 基础抽象类 - AnimalWithFur。它有一个只读属性 furColor,在后继者中被覆盖。
  2. 基类的后继者 - Cat,覆盖 furColor 并返回 gray
  3. Cat 的继任者 - Tiger,它会覆盖 furColor 并返回 striped

然后我们声明一个带有 Cat 类型参数的方法(not AnimalWithFur)。
向该方法发送Tiger 实例是否违反了 SOLID 中的 L

【问题讨论】:

    标签: oop solid-principles liskov-substitution-principle


    【解决方案1】:

    严格来说,是的。 Liskov的wiki文章总结说:

    “...在一个程序中...不改变那个程序的任何理想属性

    如果您回到 Barbara Liskov 的 original paper,它的措辞字面上更严格3.3。 类型层次结构

    如果对于每个 S 类型的对象 o1 都有一个 T 类型的对象 o2 使得对于根据 T 定义的所有程序 P,P 的行为不变 当 o1 代替 o2 时

    (强调我的)

    因此,如果您将 Cat 的一个实例替换为另一个执行不同操作的实例,即返回剥离而不是灰色,那么这在原始意义上是违反 Liskov 的,因为可以轻松定义依赖于颜色的程序是灰色的,在这里:

    program(Cat c){
       println(c.furColor);
    }
    

    如果您将Tiger 传递给该程序而不是Cat,则该程序的行为将改变

    但是,在应用 LSP 的正常方式中,如果您没有添加额外的前置条件或后置条件,则不构成违规。这是一个更实用、更不学术的定义,因为人们接受当用另一种具体类型替换一个具体类型的实例时,您确实打算改变程序的行为,同时保持该程序的理想属性。因此,假设客户端代码可以像处理任何其他颜色一样处理剥离,并且程序的“理想”属性不需要灰色,那么它不会违反。

    【讨论】:

    • 将参数类型重写为 AnimalWithFur 会改变什么吗?
    • 否,因为 furColour 属性在 AnimalWithFur 上。而且您仍然在用老虎代替猫,因为我必须假设,如果您考虑 LSP,那么您就是在替代并且只有两种具体类型;猫和老虎。
    • 属性的实际定义位置甚至可能无关紧要,如果它改变了程序所需的属性,那就是违反了 LSP。
    • LSP 现在有不同的风格了?这非常方便;它允许我们通过同时说是和否来回答 LSP 问题。一切都违反了strict LSP,但大多数事情都遵循normal LSP。
    • @jaco0646 我有,我引用了原论文的原文。如果猫的颜色被子类型改变,并且改变程序的行为那么它违规。维基被淡化了,说“改变理想的属性”代替“不变”。就是这两个。告诉我,你对OP的情况是否违规有什么看法?
    【解决方案2】:

    简短回答:不一定。根据您提供的信息,我会说不。对我来说,关键是你没有说出想象中的新方法应该做什么。

    您可能会认为您在新方法中要求的行为比对类层次结构的关注更重要。

    做到这一点的一种方法是从传入的实例/参数中为新方法所需的行为定义一个接口。

    然后,您可能想要传递给该方法的任何类都可以实现该接口,并且您可以分解继承层次结构的关注点,并转向关注行为的一致性。

    【讨论】:

      【解决方案3】:

      您的问题很好地描述了为什么使用类组合而不是类继承。首先,您的代码不合逻辑-Tiger 不是您意义上的CatTigerCats family. 之一从代码的角度来看,覆盖并完全替换父类的行为是不好的设计,这实际上是 liskov 替换违规 - 您的 Cat 类意味着定义的 cat 具有一些具体的颜色,并且应用程序希望分别使用它,但是您使用不一致的类型覆盖它并更改行为。 如果你能正确描述类型层次结构,你将有抽象类型 Cat 没有实现 furColor,类型 TigerHomeCat,但 HomeCat 可以有不同的颜色,不是吗?

      如果你想有 LS 违规的简单例子,例如: 您正在使用自定义实现扩展 List 接口,返回大小始终为 10,但内部对象数量不同。每个正常的应用程序都希望使用 for 语句与 list 一起工作,但由于您违反了 LS 原则,并且 List 对象的行为与预期不同。

      【讨论】:

      • 我不完全同意你关于类层次结构的观点,但即使你的评论 - 我的代码只是一个例子,它并不是最好的解决方案 :) 它很容易理解和就我而言,与现有的并广泛使用示例进行比较。不过,我喜欢你关于作文的说明。好话
      猜你喜欢
      • 1970-01-01
      • 2015-02-15
      • 1970-01-01
      • 2017-07-26
      • 1970-01-01
      • 1970-01-01
      • 2020-04-05
      • 1970-01-01
      相关资源
      最近更新 更多