轮回中的缘- -| 回首页 | 2005年索引 | - -朝三暮四

Precondition的检测- -

                                      

如果你了解DbC(Design by Contract),你一定知道它的三个主要元素:Invariant,Precondition和Postcondition。对于它们在继承体系中的使用,有一些基本原则,那就是:Invariant和Postcondition可以被子类强化,但对于Precondition,子类只能由两个选择:保持它,或削弱它。

个中原因不需要再解释,如果你了解DbC,你肯定知道为什么。我的问题是:在编程活动中,这些规则如何被实践?

对于Invariant和Postcondition来说,解决方案很简单,把子类与父类的条件进行AND运算就好了。但对于Precondition,条件减弱却没那么容易检测。

曾经看到一个方案建议,通过OR运算进行precondition的检测。比如:

public class Base {
   /**
    * @pre: para > 0;
    */
   public void setValue(int para) {
      assert para > 0;   // precondition checking.
      this.value = para;
   }

   private int value;
}

public class Derived extends Base {
   /**
    * @pre: para > 1;
    */
   public void setValue(int para) {
      assert para > 0 || para > 1; // precondition checking.
      this.value = para;
   }
}

由于OR运算只需要二个条件中的一个通过即可,这看起来确实保持或削弱了super class中的precondition。但,由于precondition是保证method代码正确执行的前提。如果使用上述方法来检测precondition,会隐藏着危险。

比如,Derived的setValue要求para必须大于1,这样其method代码的执行才是安全的。但使用上述方案,将1传递给setValue仍然可以通过precondition检测。随后的代码执行很可能陷入一种不确定的危险状态。

那么,究竟如何做才能既保证precondition是没有被sub-class增强,又能保证通过了precondition检测之后的执行是安全的呢?

以上面的例子为参照,首先的一个事实是,如果Derived::setValue的自己定义的precondition能够通过检测,那么Derived::setValue的执行一定是安全的,所以,每个类方法都应该先检测自己定义的precondition,一旦通过检测,就可以执行自身的method代码。

其次,如果自定义的precondition检测失败,我们不能让方法代码得到执行,因为可能导致不安全的状态。此时,我们应该检测super-class中的precondition,如果它也失败,说明方法调用者,即client没有满足precondition,算是违背了contract,系统进入相应的异常处理。

但,如果super-class的precondition检测通过了,则说明Derived的设计者违背了DbC的原则:即sub-class错误的增强了setValue的precondition。所以系统应该马上中止,并给出相应的信息。

注意,上面的2种情况,前者是client违背了contract,是client代码的错误,但后者却是一个设计错误。在上面的例子中,Derived::setValue的precondition就是一个错误的设计,因为它增强了Base::setValue的前置条件。

下面是按照上述方案实现之前例子的框架代码:

public class Base {

   public void setValue(int para) {
      assert preOfSetValue(para);
      bodyOfSetValue(para);
      assert postOfSetValue(para);
   }
   protected boolean preOfSetValue(int para) {
      return para > 0;         
   }

   protected boolean postOfSetValue(int para) {
      return true;
   }

   protected void bodyOfSetValue(int para) {
      this.value = para;
   }

   private int value;
}

public class Derived extends Base {

   private boolean preOfSetValueNew(int para) {
      return para > 1;
   }

   protected boolean preOfSetValue(int para) {
      if(preOfSetValueNew(para))
         return true;

      if(super.preOfSetValue(para)) {
          System.err.println("Invalid precondition of Derived::setValue");
          assert false;
      }

      return false;
   }
  
}

- 作者: 上帝没发笑 2005年03月4日, 星期五 07:41 加入博采

Trackback

你可以使用这个链接引用该篇文章 http://publishblog.blogchina.com/blog/tb.b?diaryID=865990

回复

评论内容: