首页 / 软件开发 / JAVA / 诊断Java代码: 臆想实现错误模式,第2部分
诊断Java代码: 臆想实现错误模式,第2部分2011-02-11 IBM Eric E. Allen臆想实现重温回想一下 上次接口的 臆想实现是一个合法的实现,但不满足接口规范的某些未经检查的方面。我们考虑一下下面的堆栈接口,以及许多未被其单独的类型签名捕获的不变量:清单 1. 一个堆栈接public interface Stack {
public Object pop();
public void push(Object top);
public boolean isEmpty();
}例如,请考虑我们希望任意堆栈实现都遵守的下列规则:如果一个对象 o 被压进堆栈 s ,且在堆栈上执行的下一个操作是 pop ,则该操作的返回值将为 o 。对于一个给定的堆栈 s ,如果 s.isEmpty() 的返回值为 true ,且在堆栈上执行的下一个操作是 pop ,那么对 pop 的调用将抛出一个 RuntimeException 异常。尽管 Java 语言在接口不变量的规范方面有限制,但指定象这样的添加的接口不变量还是可能的。就象我们将看到的那样,您可以用这种可以自动检查以查看接口实现是否满足它们的方法来指定这些不变量。断言向程序中添加 断言是一个很老但未被充分利用的好主意。这种思想是在程序执行的不同阶段置入某些条件的布尔检查。根据 design by contract思想,断言应该被包含在接口实现与外部客户达成的协议中。通常情况下,断言使用下面 3 种变化形式之一:前提条件检查在进入代码块之前某些条件是否成立。后置条件检查在退出代码块时某些条件是否成立。不变量检查在代码块执行 期间是否具备某些条件。由于它们的代价问题,这类断言极少以其最常规的形式受支持。相反,允许程序员检查代码块执行的某个 点是否具备各种条件。对于不给定实现代码的接口规范,前两种是最有用的。引入了基于 Java 的预处理器,如 iContract 之后,就有可能将断言放入源代码中并使它们自动转换为进行检查以确保断言永远有效的 Java 代码。由于经这些工具处理过的断言在原始文件中被指定为 Javadoc 注释,我们就可以很轻易地编译该文件,而无须运行预处理器,为未检查任何断言的代码制作一个“产品”副本。但用这种方法除去断言太过频繁。除对性能影响至关重要的部分之外,在程序的其它所有部分,断言检查的系统开销都不会很大。将断言留在程序中,可使得诊断来自最终用户的错误报告更加容易(肯定 将会有错误报告)。在我们的堆栈示例中,我们可以向 pop pop 添加一个断言,以确保 pop 永远不会在空堆栈上被调用:清单 2. 测试堆栈接口的一个断言public interface Stack {
/**
*@pre ! this.isEmpty()
*/
public Object pop();
public void push(Object top);
public boolean isEmpty();
}向接口代码中添加类似这样的断言有助于确保当调用实现方法时,这种附加的不变量有效。因为它们可被编译成代码,所以它们是快速诊断臆想实现发生的高效方法。而且,它们还可作为接口的附加文档。但是,由于它们是严格的函数布尔表达式,它们被限制在自己的表达中 ― 例如,我们将如何把堆栈的第一条规则编写到断言中?与类型声明相同,断言自身表达能力不够,无法捕捉我们可能希望在接口上指定的全部规则。由于这个原因,最好是将它们与单元测试协力使用。