通过合同与断言进行部分设计

时间:2011-02-26 09:14:05

标签: java language-agnostic assertions design-by-contract

我想就合同部分实施设计的想法得到一些意见。 目标是在不需要外部库的情况下,为不提供简单版本的合同(不变量和后置条件)添加语言。

我的例子是用Java编写的,但我认为这个想法适用于很多OO语言。

我们有一个这样的课程:

class myClass{
    type1 field1;
    type2 field2;

    public myClass(type1 param1){
        //do something
    }

    public type3 method1(type1 param1, type3 param2){
        if (paramsAreNotOk()){
            throw new IllegalArgumentException();
        }
        // do a lot of things
        return //do something
    }
}

我们以这种方式扩展上面的代码:

class myClass{
    type1 field1;
    type2 field2;

    public myClass(type1 param1){
        //do something

        assert invariant();
    }

    public type3 method1(final type1 param1, final type3 param2){
        assert invariant();
        myClass old;
        assert ((old = this.clone()) != null)

        if (paramsAreNotOk()){
            throw new IllegalArgumentException();
        }
        //do a lot of things
        type3 res = //do something

        assert method1_post(old, param1, param2, res);
        assert invariant();
        return res;
    }

    protected boolean invariant(){
        // states something about myClass and return a boolean
        // OR
        // uses some assertions on some helping methods
    }

    protected boolean method1_post(myClass old, type1 param1, type3 param2, type3 res){
        // states something about res and about the modifications made on old
        // OR
        // uses some assertions on some helping methods
    }
}

这种方法的局限性:
     - 没有先决条件      - 合同不是继承的(但请注意,不变量和后置条件受到保护,可以由子类重复使用。)
     - 没有检查不变量和后置条件不会修改对象的状态,因此存在副作用的风险。
     - 合同不是我们文件中明确的一部分      - 我们需要为每个班级制作克隆。

现在,有些问题:
     - 这种方法会以任何方式伤害表演吗?我的意思是,如果断言被禁用,甚至旧的和res局部变量也会被JIT编译器删除?      - 你认为这种做法有什么缺点吗?你为什么不在课堂上使用它?      - 你能建议任何改进吗?

感谢您的阅读和您的意见。

3 个答案:

答案 0 :(得分:2)

如果你想要Java的“按合同设计”,你可能想看看(真的)大家伙是怎么做的!以下是Google最近对“Contracts for Java”主题的讨论:

http://google-opensource.blogspot.com/2011/02/contracts-for-java.html

现在回答你的两个问题:

- do you see any downside of this approach? Why wouldn't you use this in your classes?

因为一个缺点是它非常冗长:如此冗长以至于使代码几乎无法读取。

- can you suggest any improvement?

不要重新发明轮子......

答案 1 :(得分:2)

这并不可怕,实际上它是在你面前被别人写的。例如,请参阅Liskov / Guttag的Program Development in Java,它将您的方法用于不变检查,但将其称为repOK()而不是invariant()。

在有限的应用程序中,它有点有用。但是,合同规范不必担心实际代码所做的“谁在调用谁”这一事实,这就产生了许多问题。

  • 假设您有一些方法F,它调用另一种方法G.想象一下F在运行时打破了rep不变量,但在它返回之前修复了它。这是允许的,在某些情况下是必需的,但G不知道,并且它将错误地引发异常。
  • 建设者更糟糕。假设类D扩展了C类并覆盖了invariant()。 D()调用C(),调用D.invariant(),这是错误的。 C不必满足D的不变量,这比它自己的强度要强。
  • 如果一个方法被类外的客户端传递错误的参数,那么IllegalArgumentException就可以了。但如果调用者在课堂内,这是一个常规的旧合同违规。你想区分这两者。如果你有兴趣,Gary Leavens会谈到JML如何在paper中完成它。
  • 以其他类方法表示的后置条件(即“后置条件”)将在检查时导致无限的相互递归。

我认为DbC很有意思,如果语言有它(或者更好,像Python的函数装饰器那样),或者你有Modern Jass这样的工具,那么就深入研究。但是这样做纯Java是不可行的。也就是说,我正在研究一个不变的检查tool,它会产生类似于你在这里的代码,减去调用链问题(它可以通过扩展类来接受一个知道什么时候适合的访问者来工作。做检查)。它需要Eclipse,并且有自己的问题(主要是私有和静态相关的坏词),但检查机制是纯Java。

答案 2 :(得分:0)

  你知道这有什么缺点吗?   进场?你为什么不用这个呢?   你的课程?

我编写的大多数Java类都不可克隆,因为在Java中实现Clonable并不容易。因此,当不是绝对必要时,我不会实施它。我不想只为你的方法做这件事。