10

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

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

以下是加强前置条件削弱后置条件的示例,因此违反了 LSP(链接):

  1. 假设您的基类与成员 int 一起使用。现在您的子类型要求该 int 为正数。这是强化的先决条件,现在任何以前使用负整数都可以正常工作的代码都被破坏了。

  2. 同样,假设相同的场景,但用于保证成员在被调用后为正的基类。然后子类型改变行为以允许负整数。对对象起作用的代码(并假设后置条件是一个正整数)现在被破坏了,因为后置条件没有得到支持。

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

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

例子:

基类后置条件保证方法的返回值在范围内1-10,但随后子类型将后置条件更改为只允许返回值在范围内2-9。现在,处理从此方法返回的对象的代码(并假设后置条件在一个范围内1-10)被破坏了,因为后置条件没有得到支持。

4

5 回答 5

3

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

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

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

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

于 2015-09-22T09:22:25.370 回答
-1

如果方法的基类约定说用负数调用它会引发异常,那么任何合法的派生类都应该在其方法传递负数的情况下引发相同的异常。然而,如果基类契约只是简单地说一个方法不能用负数调用而不指定如果它是会发生什么,那么基类可以做它喜欢做的任何事情而不会破坏该契约。

虽然存在未指定类的行为的条件似乎不优雅,但在保持向上兼容的同时向类添加功能通常需要将未指定的行为更改为指定的行为。在某些情况下,类上的“未指定”行为可能是“无法编译”,但不能保证尝试使用此类成员的代码总是无法编译。例如,如果一个类的契约没有提及成员“Foo”,那么尝试使用这样的成员可能会导致编译错误,但该类的未来版本可以在Foo不违反其契约的情况下进行定义。

于 2013-07-29T22:08:54.787 回答
-1

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

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

后置条件规则实际上是说:

这条规则说,子类型方法提供的不仅仅是超类型方法:当它返回时,超类型方法将提供的所有内容都得到保证,并且可能还有一些额外的效果(Java 中的程序开发,p177)

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

于 2013-06-13T07:20:16.043 回答
-1

LSP 意味着您应该能够将基类替换为子类以获取规范中的输入值。当您首先违反基类的合同时,比较基类与子类的行为是没有意义的(如果这样做,您就不会处理真正的替换)。

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

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

于 2014-12-03T22:41:02.910 回答
-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 明显违反:Weak class for 101-1000 ok, but Base class for 101-1000 throws exception。这显然不是相同的行为。

LSP 期望,基类的数字集合可以在子类中扩大(加强),但在主程序中,该集合不会扩大,因此前置条件较弱的子类可以满足基类的前置条件。

反过来,后置条件也是如此。

于 2014-10-08T23:55:23.460 回答