不能削弱先决条件和加强后置条件也违反Liskov替代原则吗?

时间:2013-05-08 17:42:48

标签: inheritance liskov-substitution-principle preconditions post-conditions

子类型的实际前提条件是通过组合(使用逻辑OR基本类型的前提条件和子类型的前提条件来创建的。 em>,这使得产生的前提条件 限制性较小

子类型的实际后置条件是通过组合(使用逻辑AND基本类型的后置条件子类型的后置条件来创建的,这会使产生后置条件 更具限制性

以下是 加强 前提条件 弱化 的示例后置条件,结果违反了LSP(Link):

  
      
  1. 假设您的基类使用成员int。现在你的子类型要求int为正数。这是前期条件的加强,   现在任何代码在使用负数之前都能正常工作   已经坏了。

  2.   
  3. 同样,假设相同的场景,但基类用于保证成员在被调用后是正面的。然后   子类型更改行为以允许负的int。代码那个   适用于该对象(并假设后置条件是积极的   由于不支持后置条件,因此现在已经破坏了。

  4.   

a)当被覆盖的方法 弱化 前提条件时,为什么它也不被视为违反LSP,因为此方法可以使用参数基本类型的合同是不可接受的。因此,我们不能声称违反了基类型的合同,因此LSP也被违反了吗?

b)当被覆盖的方法 强化 后置条件时,为什么它也不被视为违反LSP,因为调用此方法的客户端将仅接收原始方法的可能结果的子集。因此,我们不能声称违反了基类型的合同,因此LSP也被违反了吗?

示例:

基类后置条件保证方法的返回值在范围1-10内,但子类型更改后置条件仅允许返回值2-9范围内。现在,对于从此方法返回的对象(并假设后置条件在范围1-10内)的代码已被破坏,因为后置条件未得到维护。

5 个答案:

答案 0 :(得分:3)

抱歉,您的考虑因素存在逻辑错误。

  

基类后置条件保证方法的返回值   将在1-10范围内,但随后子类型改变了   postcondition只允许返回值在2-9范围内。

由于代码在1-10范围内工作,范围2-9 实际上在1-10范围内,强化后置条件应该永远不会成为问题。

同样削弱前提条件。允许子类型接受更大的范围不会破坏基本类型的行为。由于行为仅在子类型中引入,并且仅作为子类型方法的前提条件。

答案 1 :(得分:-1)

我认为你的例子在以下意义上不支持你的观点。

在负int,正int示例中,添加负数虽然包含更多可能性,但会削弱后置条件的保证。你的例子也做同样的事情。虽然它保证了更窄的范围,但它仍然削弱了基类提供的后置条件的保证。

条件后规则实际上说:

这条规则说子类型方法提供的不仅仅是超类型方法:当它返回超类型方法提供的所有内容时,也可以确保一些额外的效果(Java中的程序开发, P177)

您的示例并不保证超类型保证的所有内容。我想在你的例子中,强化意味着子类型保证返回的int在1-10中,此外它保证返回的int在2-9之间,而不仅仅是第二个。

答案 2 :(得分:-1)

如果某个方法的基类合同表明使用负数调用它会抛出异常,那么任何合法的派生类如果传递的是负数,则应抛出相同的异常。但是,如果基类合同只是说一个方法不能用负数调用而没有指定它会发生什么,那么基类可以做任何它喜欢的事情而不破坏该合同。

虽然在没有指定类的行为的情况下看起来似乎不太优雅,但是在保持向上兼容的同时向类添加功能通常需要将未指定的行为更改为指定的行为。在某些情况下,类上的“未指定”行为可能“无法编译”,但无法保证尝试使用此类成员的代码始终无法编译。例如,如果类的合同未提及成员“Foo”,则尝试使用此类成员可能会导致编译错误,但该类的未来版本可能会定义Foo而不违反其合同

答案 3 :(得分:-1)

LSP意味着您应该能够使用子类替换基类以获取符合规范的输入值。当你在第一个位置违反基类的契约时,比较基类和子类的行为是没有意义的(如果你这样做,你就不会处理真正的替换)。 p>

(违反合同是未知的行为,只是碰巧最常见的方法是抛出异常。)

关于加强后置条件,我不明白你的意思,真的(?)如果合同规定的值是1-10,那么1-10之间的任何值都是事实上的规范,也是2-9甚至3总是(?)

答案 4 :(得分:-2)

你是对的。前提条件不能削弱。这也会改变基类型的行为。例如:

class Base { void method(int x) { /* x: 1-100 allowed else exception  */ } }
class Weak: Base { void method(int x) { /* x: 1-1000  allowed else exception  */ } }
class Strong: Base { void method(int x) { /* x: 1-10  allowed else exception  */ } }

int Main() {
  Base base = new Base();
  base.method(101-1000); // exception

  Base base2 = new Weak();
  base2.method(101-1000); // ok

  Base base3 = new Strong();
  base3.method(101-1000); // exception 
}

明显违反了LSP:弱类为101-1000 ok,但101-1000的基类抛出异常。 这显然不是同一种行为。

LSP预计,基类的数字集可以在子类中加宽(加强),但在主程序中,集合不会加宽,因此具有较弱前提条件的子类可以满足基类的前提条件。

后一种情况也是如此。