如果你了解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;
}
}
你可以使用这个链接引用该篇文章 http://publishblog.blogchina.com/blog/tb.b?diaryID=865990