3

公共方法的前置条件后置条件构成了该方法与其客户端之间的契约。

1.根据调用者不应该验证后置条件被调用方法不应该验证前置条件

让我们回忆一下平方根函数 sqrt 的前置条件和后置条件,如程序 49.2 所示。调用 sqrt 的函数负责将非负数传递给函数。如果传递了一个负数,平方根函数根本不应该做任何事情来处理它。另一方面,如果将非负数传递给 sqrt,则 sqrt 有责任提供满足后置条件的结果。因此, sqrt 的调用者根本不应该检查或纠正结果。

如果操作的前置条件失败,则归咎于调用者 如果操作的后置条件失败,则归咎于被调用的操作

但正如在另一篇文章中包含的代码中所见,被调用的方法确实验证了先决条件

/// <summary>Gets the user for the given ID.</summary>
public User GetUserWithIdOf(int id,
        UserRepository userRepository) {
  // Pre-conditions
  if (userRepository == null)
      throw new ArgumentNullException(
          "userRepository");
  if (id <= 0)
      throw new ArgumentOutOfRangeException(
          "id must be > 0");

  User foundUser = userRepository.GetById(id);

  // Post-conditions
  if (foundUser == null)
      throw new KeyNotFoundException("No user with " +
          "an ID of " + id.ToString() +
          " could be located.");

  return foundUser;
}

a)既然满足方法的先决条件是客户的责任,那么被调用的方法是否也应该 检查先决条件是否得到满足?

b) 由于被调用方法有责任交付满足后置条件的结果,调用者是否应该检查后置条件

2.第一篇文章中提到的一个好处是“前置条件和后置条件可用于在OOP中划分类之间的责任”,我理解这也是说验证前置条件不是被调用方法的责任,它调用者不负责验证postcondition

但是坚持这样的理念不会使我们的代码更容易受到攻击,因为它盲目地相信对方(对方是调用者方法)会兑现它的承诺吗?

3.如果调用者/被调用方法不盲目信任对方,那么我们不要失去很多后置条件前置条件提供的好处,因为现在被调用方法必须承担检查前置条件的责任,调用方必须承担责任验证后置条件

谢谢

编辑

3.

如果调用者/被调用方法不盲目信任对方,那么我们不要失去很多后置条件和前置条件提供的好处,因为现在被调用方法必须承担检查前置条件的责任,调用方必须承担验证的责任后置条件?

调用者不需要验证后置条件,因为它们应该由被调用的方法来确保。被调用的方法确实需要验证先决条件,因为没有其他方法可以强制执行合同。

a)您是否假设后置条件只应声明/保证返回值是指定类型或null(如果 返回值可以为)?除了返回值的类型之外,后置条件还不能说明其他事情(无法通过类型系统验证),例如返回值是否在指定范围内(例如:不能后置条件也说明返回值的类型int将在 ) 的范围内10-20?在这种情况下,客户是否还需要检查后置条件

b)那么我们可以说第一篇文章声称被调用的方法不应该检查先决条件是错误的吗?

2. 编辑

不,后置条件可以是任何东西,而不仅仅是空检查。无论哪种方式,客户端都可以假设后置条件已经过验证,例如,如果合同声明它已被确保,您不需要验证 int 范围。

a) 您之前说过,为了使代码不那么容易受到攻击,需要通过调用方法检查前置条件,但我们是否也不能推断调用方需要验证后置条件(例如,验证返回值是否在后置条件承诺的范围内)为了使调用者的代码不那么容易受到攻击?int

b)如果客户可以盲目地信任后置条件的声明(我会说当后置条件提出返回值在某个范围内的声明时,这是一种盲目信任),为什么被调用方法也不能相信调用者会满足被调用方法的先决条件

4

1 回答 1

3

a)既然满足方法的先决条件是客户的责任,那么被调用的方法是否也应该检查先决条件是否得到满足?

是的,确保满足先决条件是客户端的责任,但是被调用的方法必须验证这一点,因此示例中的 null 检查。

b)既然被调用的方法有责任交付满足后置条件的结果,那么调用者是否应该检查后置条件?

调用者应该能够依赖于被调用方法的契约。在您提供的示例中,该方法GetUserWithIdOf确保满足后置条件,否则抛出异常。存储库本身没有后置条件,即它总是返回用户,因为可能找不到用户。

2.第一篇文章中提到的一个好处是“前置条件和后置条件可用于在OOP中划分类之间的责任”,我理解这也是说验证前置条件不是被调用方法的责任,它调用者不负责验证后置条件。

验证前置条件仍然是被调用方法的责任,因为它们通常不能被类型系统验证。诸如Eiffel之类的语言提供了更大程度的静态合约验证,在这种情况下,人们可以利用该语言来强制执行先决条件。就像在 Java/C# 中您可以强制方法参数属于给定类型一样,Eiffel 将这种类型的验证扩展到更复杂的合约声明。

但是坚持这样的理念不会让我们的代码更容易受到攻击,因为它盲目地相信对方(对方是调用者或方法)会兑现它的承诺吗?

是的,这就是为什么必须验证先决条件的原因。

3.如果调用者/被调用方法不盲目信任对方,那么我们不要失去很多后置条件和前置条件提供的好处,因为现在被调用方法必须承担检查前置条件的责任,调用方必须承担责任验证后置条件?

调用者不需要验证后置条件,因为它们应该由被调用的方法来确保。被调用的方法确实需要验证先决条件,因为没有其他方法可以强制执行合同。

更新

a)不,后置条件可以是任何东西,而不仅仅是空检查。无论哪种方式,客户端都可以假设后置条件已经过验证,例如,如果合同声明它已被确保,您不需要验证 int 范围。

b) 我会说是的。去引用:

如果传递了一个负数,平方根函数根本不应该做任何事情来处理它。

if表明被调用的方法已经进行了某种验证。什么都不做是无声的失败,这可能是一种反模式

随后的报价:

如果操作的前置条件失败,则归咎于调用者 如果操作的后置条件失败,则归咎于被调用的操作

将失败的先决条件归咎于调用者的唯一方法是首先确定先决​​条件失败。由于被调用的方法“拥有”这个前提条件,它应该是标记失败的最后一站。

更新 2

a,b) 调用者可以信任后置条件的原因是因为后置条件可以通过被调用的方法来保证。被调用的方法是声明和拥有合约的方法。被调用方法不能信任调用者的原因是没有人保证满足先决条件。被调用的方法不知道它可能拥有的所有各种调用者,因此它必须自己验证。

于 2013-05-01T18:57:02.417 回答