接口和抽象方法中的前提条件和后置条件

时间:2017-02-26 16:11:33

标签: interface abstract-class design-by-contract

我正在尝试实现自己的编程语言,而且我正在进行lexing和解析。我差不多完成了,并希望为类不变量,前置条件和后置条件添加原生支持。

public withdraw (d64 amount) : this {

    require amount > 0;
    require this.balance - amount > this.overdraft;

    # method code

    d64 newBalance = this.balance - amount;
    ensure this.balance == newBalance;
} 

您还可以在类的顶部定义类不变性。

class BankAccount {
    invariant this.balance > this.overdraft;
    # class body
}

这是我的问题:

  1. 在抽象类或接口中包含类不变性是否有意义。
  2. 在抽象方法和接口方法中包含前置条件是否有意义。
  3. 在抽象方法或接口方法中包含后置条件是否有意义。
  4. 我自己想一想,我认为在接口中包含不变性或后置条件并不合理,但我并没有真正看到前提条件的问题。

    可以在下面的抽象和接口方法中包含前置条件和后置条件。

    public interface BankAccount {
        public withdraw (d64 amount) : this {
    
            require amount > 0;
            require this.balance - amount > this.overdraft;
    
            # no other statements (implementation)
    
            d64 newBalance = this.balance - amount;
            ensure this.balance == newBalance;
        }
    }
    

1 个答案:

答案 0 :(得分:0)

这实际上取决于您的界面是有状态还是无状态。包含接口方法的前置和/或后置条件可能非常好。事实上,我们一直这样做。无论何时创建一个javadoc(或任何其他工具),您都要创建一个合同。否则,你怎么测试什么?重要的是要意识到测试驱动开发和按合同设计有很多共同之处。定义合同对于正确的tdd至关重要 - 您首先要设计一个接口并为其创建非正式合同(使用人类可读的语言)。然后,您编写测试以确保合同得到满足。如果我们遵循tdd classicists(https://www.thoughtworks.com/insights/blog/mockists-are-dead-long-live-classicists),我们总是针对合同编写测试。

现在,更具体一点。如果接口是有状态的,我们可以根据其他方法轻松表达其不变量。我们以java List接口为例:

如果你仔细阅读javadoc,你会发现有很多不变量。例如,add方法具有以下合同:

  1. 前提条件:元素不能为空(如果列表不支持它 - 在我看来,这是一种设计气味,但让我们把它放在一边 现在)

  2. 后置条件:保留排序,即其他排序 元素无法更改

  3. 由于List接口肯定是有状态的,我们可以使用查询方法来推断列表的状态,例如getsublist等。因此,您可以表达所有不变量在接口的方法上。

    如果接口是无状态的,例如Calculator,我们也定义了一个契约,但它的不变量不包含任何状态。因此,例如,sum方法可以具有以下合同:

    int sum(int a, int b)
    Preconditions: a and b are integers (which is automatically guaranteed by static type checking in Java)
    Postconditions: the result is an integer (again - type safety) which is equal to a + b
    

    我们的Calculator是无状态接口,因此我们的不变量中不包含任何状态。

    现在,让我们回到您的BankAccount示例:

    您描述的方式,BankAccount绝对是一个有状态的界面。实际上,它是我们称之为Entity(在域驱动设计方面)的模型示例。因此,BankAccount拥有它的生命周期,它是状态,并且可以(并且将会)在其生命周期内发生变化。因此,根据班级的状态方法表达合同是完全正确的。您需要做的就是将amountbalanceoverdraft移动到界面的顶部,作为属性(如果您的语言支持它)或方法 - 它不会'真的很重要。重要的是amountbalanceoverdraft现在是您界面的一部分,并形成界面的无所不在的语言 。这些方法/属性是整个BankAccount接口的组成部分 - 这意味着它们可以作为接口合同的一部分。

    前段时间我实现了一个非常简单的Java契约原型,实现为面向方面编程支持的注释集。我试图实现与你类似的目标 - 将合同与语言结合起来,使其更加正式。这只是一个非常简单的原型,但我认为它很好地表达了这个想法。如果你有兴趣 - 我应该很快将它上传到github(我到目前为止大部分时间都在使用bitbucket)。