覆盖虚拟布尔纯方法而不破坏LSP

Ser*_*046 12 oop design-patterns liskov-substitution-principle solid-principles

例如,我们有以下结构:

class Base
{
    [pure]
    public virtual bool IsValid(/*you can add some parameters here*/)
    {
       //body
    }
}

class Child : Base
{
    public override bool IsValid(/*you can add some parameters here*/)
    {
       //body
    }
}
Run Code Online (Sandbox Code Playgroud)

你能否填写Base::IsValid()Child::IsValid()与不同的机构,但没有与LSP的冲突?让我们想象它只是分析的方法,我们无法改变实例的状态.我们能做到吗?我对任何一个例子感兴趣.在一般情况下,我试图了解虚拟(身体)布尔方法是否是反模式.

Mik*_*ike 5

LSP的想法并不禁止子类的多态性.相反,它强调允许改变什么,不改变什么.一般来说,这意味着:

  1. 任何重写函数都接受并返回相同类型的重写函数; 包括可能抛出的异常(输入类型可能会扩展被覆盖的类型和输出类型可能会缩小它们 - 这仍将保持这种限制).
  2. "历史规则" - Child对象的"Base"部分不能被Child的函数更改为使用Base类函数永远无法访问的状态.因此,期望Base对象的函数永远不会得到意外的结果.
  3. 不得在儿童中更改基地的不变量.也就是说,关于Base类行为的任何一般假设必须由Child保留.

两个第一颗子弹的定义非常明确."不变量"更多地是一种感觉问题.例如,如果实时环境中的某些类要求其所有函数在某个恒定时间内运行,则其子类型中的所有重写函数也必须遵守该要求.

在您的情况下,IsValid()意味着什么,并且必须在所有子类型下保留"某些东西".例如,假设您的Base类定义了一个产品,而IsValid()则告诉该产品是否有效销售.每件产品的确切含义可能会有所不同.例如,它必须设置其价格才能有效销售.但是儿童产品在出售之前还必须经过电力测试.

在这个例子中,我们保留了所有要求:

  1. 函数的输入和输出类型不会更改.
  2. Child对象的Base-part的状态不会以Base类无法预期的方式更改.
  3. 保留了类的不变量:仍然没有出售没有价格的Child对象; 无效的含义仍然是相同的(不允许出售),它只是以与儿童相匹配的方式计算.

您可以在这里获得更多解释.

===

编辑 - 根据笔记的一些其他解释

多态性的整个想法是每个子类型以不同的方式完成相同的功能.LSP不违反多态性,但描述了多态性应该处理什么.特别是,LSP要求任何子类型Child都可以在代码需要的地方使用,Base并且任何假设都Base适用于他Child的任何一个.在上面的例子中,IsValis() 并不意味着"有价格".相反,它恰恰意味着:产品是否有效?在某些情况下,有一个价格就足够了.另外,它还需要电力检查,而在其他情况下,它可能还需要一些其他属性.如果Base该类的设计者不要求通过设定价格使产品变得有效,而是IsValid()作为单独的测试离开,则不会发生违反LSP的情况.什么样的例子会造成这种违规行为?一个例子,其中一个人询问一个对象IsValid(),然后调用一个不应该改变有效性的基类的函数,并且该函数变为Child不再有效.这违反了LSP的历史规则.这里由其他人提供的已知示例是矩形的子矩形.但只要相同的函数调用序列不需要特定的行为(同样 - 没有定义设置价格使产品有效;它恰好在某些类型中就是这样) - LSP按需要保留.