通过带有断言的契约进行部分设计

发布于 2024-10-19 17:35:02 字数 1884 浏览 7 评论 0原文

我想就部分实施合同设计的想法征求一些意见。 目标是添加到不提供轻量版本契约(仅不变量和后置条件)的语言,而无需外部库。

我的示例是用 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 编译器删除?
- 您认为这种方法有什么缺点吗?你为什么不在课堂上使用它?
- 你能提出任何改进建议吗?

感谢您的阅读和意见。

I would like to get some opinions on an idea for a partial implementation of design by contract.
The goal is to add to the languages that don't offer it a light version of contracts (invariants and post conditions only) without the need of an external library.

My example is written in Java but I suppose that the idea is good for a lot of OO languages.

We have a class like this:

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

We extend the code above in this way:

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

Limitations of this approach:
- no pre-conditions.
- the contract is not inherited (but please note that invariant and post-conditions are protected and can be reused by a subclass).
- there isn't any check that invariant and post-conditions don't modify the state of our object, hence there is a risk of side effects.
- the contract is not part of our documentation in a clear way.
- we need to make cloneable every class.

Now, some questions:
- does this method hurt the performances in any way? I mean even the old and res local variables are removed by the JIT compiler if assertions are disabled?
- do you see any downside of this approach? Why wouldn't you use this in your classes?
- can you suggest any improvement?

Thank you for your reading and for your opinions.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

八巷 2024-10-26 17:35:02

这并不可怕,事实上在你之前其他人已经写过它了。例如,请参阅 Liskov/Guttag 的 Java 中的程序开发,它采用了您的方法不变检查,但称之为repOK()而不是invariant()。

在有限的应用中,它有点起作用。但是,由于合约规范不必像真实代码那样担心“谁在调用谁”的问题,因此产生了很多问题。

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

我的看法是 DbC 很有趣,如果该语言有它(或者更好的是,像 Python 的函数装饰器之类的东西),或者你有一个像 现代 Jass,然后深入研究。但是用纯 Java 来做是不可行的。也就是说,我正在开发一个不变检查工具,它生成的代码与您这里的代码类似,减去调用链问题(它的工作原理是扩展类以接受知道何时适合进行检查的访问者)。它需要Eclipse,并且有自己的问题(主要是与private和static等坏词有关),但检查机制是纯Java的。

It's not horrible, and in fact it's been written about by others before you. For instance, see Liskov/Guttag's Program Development in Java, which takes your approach to invariant checking, but calls it repOK() rather than invariant().

In a limited application, it kinda-sorta works. But there are a lot of problems that come out of the fact that contract specifications don't have to worry about the sort of "who's calling who" problems that real code does.

  • Say you have some method F, which calls another method G. Imagine that F breaks the rep invariant while it runs, but fixes things before it returns. This is allowed, and in some cases required, but G doesn't know that, and it will incorrectly raise an exception.
  • Constructors are worse. Say class D extends class C and overrides invariant(). D() calls C(), which calls D.invariant(), which is wrong. C does not have to satisfy D's invariant, which is stronger than its own.
  • If a method is passed the wrong arguments by a client outside the class, then IllegalArgumentException is fine. But if the caller is inside the class, this is a regular old contract violation. You want to distinguish between the two. Gary Leavens talks about how JML does it in this paper, if you're interested.
  • Postconditions expressed in terms of other class methods (that is, "postconditions") will cause infinite mutual recursion when checked.

My take is that DbC is interesting, and if the language has it (or, even better, something like Python's function decorators), or you have a tool like Modern Jass, then dig in. But doing it in pure Java isn't feasible. That said, I'm working on an invariant checking tool that generates code similar to what you have here, minus the call-chain issues (it works instead by extending the classes to accept a visitor which knows when it's proper to do the check). It requires Eclipse, and has problems of its own (mainly related bad words like private and static), but the checking mechanism is pure Java.

吲‖鸣 2024-10-26 17:35:02

如果您想要 Java 的“契约设计”,您可能想看看(真正的)大佬是如何做的!以下是 Google 最近对“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?

不要重新发明轮子...

If you want "Design By Contract" for Java, you may want to take a look at how the (really) big guys are doing it! Here's Google's recent take on the subject with "Contracts for Java":

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

Now to answer two of your questions:

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

Because one downside is that it is highly verbose: so much verbose as to make the code barely readable.

- can you suggest any improvement?

Don't reinvent the wheel...

童话里做英雄 2024-10-26 17:35:02

你认为这有什么缺点吗
方法?你为什么不使用这个
你的课吗?

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

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

Most Java classes I write are not clonable, because it's not easy to implement Clonable right in Java. Therefore I don't implement it when it's not absolutely necessary. I wouldn't want to have to do this just for your approach.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文