今天这篇博客我们来讨论一下关于ADT中规约(spec)的问题。
这里的重点有:第一个就是什么是规约,行为等价性,然后是前置后置条件的论述,以及规约的强度。
规约简单来说就是一份合同,一份契约:它规定了ADT的实现者需要按照规约进行实现,而客户则像使用说明书一样来使用规约,spec给实现者以及客户端两方都提供了要求。
好的,那我们首先就来稍微讨论一下为什么会存在规约这个东西?有这么几点:第一呢是说很多的bug都是来源于实现者和客户端之间的误解,两段代码的接口行为由于误解而产生的bug。第二点我们说,仅仅是不同的开发者之间,每一个人都是有不同的想法,需要使用规约来对大家的做法进行统一。
行为等价性是我们下一个要论述的东西:ADT之间是否可以相互替换?我们应该站在客户端的角度来进行观察,如果两个不同的ADT的实现所需要的输入以及产生的输出影响都是一样的,那我们就可以说他们具有行为等价性。那么简单的判定方式什么呢:利用规约来判定行为是否等价(一定要注意:规约中不应当包括局部变量,私有字段以及ADT的实现细节)
那么前置条件(requires)和后置条件(effect)又怎么来理解呢?根据二者的英文关键词我们也能略知一二:前置条件指的是对客户端的约束:在调用这个方法的时候必须要满足什么条件。对于后置条件来说,值得是对开发者实现者的约束:在这个方法结束的时候必须要满足什么条件。在这里有一个必要的关系必须得到满足:一旦客户端按照前置条件的要求进行了合法的输入,那么后置条件必须给予满足,这个满足不一定是说一定能够返回合适的值,抛出了指定的异常等等也是合理的满足。
那么更深的了解,对于Java呢?Java中的规约声明是什么样子的?一般大概看成两类:一类是静态类型声明,是由编译器自动检查和执行的部分,另一类是方法前的注释,它们也是一种规约,但是需要人工判定@param @return @throws是否满足。
Java建模语言(Java Modeling Language,JML)是一种正式合同规约在Java代码中增加了一些符号,这些符号用来标识一个方法是干什么的,却并不关心它的实现。如果使用JML的话,我们就能够描述一个方法的预期的功能而不管他如何实现。通过这种方式,JML把过程性的思考延迟到方法设计中,从而扩展了面向对象设计的这个原则。
JML标记总是在Java注释的内部,所以对正常编译的代码没有任何影响。如果你想比较一下普通的类和使用了JML的类有什么差别的话,你可以使用一个开源的JML编译器。用JML编译器编译的代码如果没有实现JML规范所要求的事项,运行时就会抛出一个JML异常。这个特性不仅可以帮助我们捕获代码中的bug,而且可以确保JML形式的文档可以与程序代码高度一致。
而Javadoc 是SUN公司提供的一个技术,它从程序源代码中抽取类、方法、成员等注释形成一个和源代码配套的API帮助文档。也就是说,只要在编写程序时以一套特定的标签作注释,在程序编写完成后,通过Javadoc就可以同时形式程序的开发文档了。
下一个我们重点讨论的问题就是:规约是有强有弱的,怎么判定一个规约强还是弱呢?整体我们可以这么进行判定:更放松的前置条件+更严格的后置条件 == 更强的spec 换句话说对客户端越友好,对客户端使用时候的要求越少,我们的规约也就是越强的。规约是给实现者制定的:越强的规约,实现者的自由度越低责任越重,客户端责任也就越低。
这就是我们今天所要讨论的ADT中的spec。