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

Interface 接口和抽象方法中的前置条件和后置条件,interface,abstract-class,design-by-contract,Interface,Abstract Class,Design By Contract,我正在尝试实现自己的编程语言,目前正在进行词法分析和解析。我几乎完成了,希望添加对类不变量、前置条件和后置条件的本机支持 public withdraw (d64 amount) : this { require amount > 0; require this.balance - amount > this.overdraft; # method code d64 newBalance = this.balance - amount;

我正在尝试实现自己的编程语言,目前正在进行词法分析和解析。我几乎完成了,希望添加对类不变量、前置条件和后置条件的本机支持

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
}
以下是我的问题:

  • 在抽象类或接口中包含类不变性是否有意义
  • 在抽象方法和接口方法中包含前置条件是否有意义
  • 在抽象方法或接口方法中包含后条件是否有意义
  • 我自己想一想,我认为在接口中包含不变性或后置条件是没有意义的,但我真的不认为前置条件有什么问题

    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;
    } 
    
    可以在抽象和接口方法中包括前置和后置条件,如下所示

    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;
        }
    }
    

    这实际上取决于您的接口是有状态的还是无状态的。为接口方法包含前置和/或后置条件是非常好的。事实上,我们一直在这样做。任何时候创建一个javadoc(或任何其他工具),都是在创建一个契约。否则,你怎么能测试任何东西?重要的是要认识到测试驱动的开发和契约式设计有很多共同点。定义契约对于正确的tdd是至关重要的——您首先要设计一个接口并为它创建一个非正式契约(使用人类可读的语言)。然后,编写一个测试以确保合同得到满足。如果我们遵循tdd classisits(),我们总是根据契约编写测试

    现在,更具体地说。如果接口是有状态的,我们可以根据其他方法很容易地表达它的不变量。让我们以java <代码>列表<代码>界面为例:

    如果您仔细阅读javadoc,您将看到有很多不变量。例如,
    add
    方法具有以下契约:

    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
    
  • 前提条件:元素不能为null(如果列表不支持它- 顺便说一句,在我看来,这是一种设计的味道,但让我们把它放在一边 (现在)

  • 后置条件:保留排序,即其他条件的排序 元素不能更改

  • 由于
    List
    接口肯定是有状态的,所以我们可以使用查询方法对列表的状态进行推理,如
    get
    子列表
    等。因此,您可以根据接口的方法表达所有不变量

    对于无状态的接口,例如
    计算器
    ,我们也定义了一个契约,但它的不变量不包括任何状态。因此,例如,
    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
    
    我们的
    计算器
    是一个无状态接口,因此在不变量中不包含任何状态

    现在,让我们回到您的
    银行账户
    示例:

    按照您的描述方式,
    BankAccount
    绝对是一个有状态的接口。事实上,它是我们称之为
    实体的一个模型示例(就领域驱动设计而言)。因此,
    BankAccount
    有它的生命周期,它的状态,在它的生命周期中可以(也将)改变。因此,基于类的state方法来表示契约是非常好的。您所需要做的就是将您的
    金额
    余额
    透支
    作为属性(如果您的语言支持)或方法移动到界面顶部-这并不重要。重要的是,
    金额
    余额
    透支
    现在已成为界面的一部分,并构成界面的普遍语言。这些方法/属性是整个
    BankAccount
    接口的组成部分,也就是说,它们可以用作接口合同的一部分


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

    为什么先决条件可以,而不是后决条件?@Lee因为如果接口中没有实现,您会使用什么来签入后决条件?在上面的示例中,您不知道是否存在
    this.balance
    。不变量只与成员相关,因此如果接口可以具有属性,则可以声明关于它们的不变量。接口方法可以像任何其他方法一样具有后置条件。@Lee感谢您的回答