软件构造知识点总结(二)规格说明、异常与不变类型
1.规格说明
规格说明是团队合作中的关键点。如果没有规格说明,就没有办法分工实现各种方法。规格说明就像一份合同:实现者的义务在于满足合同的要求,客户可以依赖这些要求工作。事实上,我们会发现就像真的合同一样,规格说明对双方都有制约:当合同上有前置条件时,客户有责任满足这些条件。
如上图所示,规格说明就好像一道防火墙一样将客户和实现者隔离开。它使得客户不必知道这个单元是如何运行的(不必阅读源码),也使得实现者不必管这个单元会被怎么使用(因为客户要遵守前置条件)。这种隔离造成了“解耦”(decoupling),客户自己的代码和实现者的代码可以独立发生改动,只要双方都遵循规格说明对应的制约。
1.1规格说明的结构
一个规格说明一般含有以下两个“条款”:
- 一个前置条件,关键词是requires
- 一个后置条件,关键词是effects
其中前置条件是客户的义务(谁调用的这个方法)。它确保了方法被调用时所处的状态。而后置条件是实现者的义务。如果前置条件得到了满足,那么该方法的行为应该符合后置条件的要求,例如返回一个合适的值,抛出一个特定的异常,修改一个特定的对象等等。
由于Java对于注释文档有一些要求,所以Java的规格说明一般遵循以下结构:
/**
*@param xxxxxx
*@param xxxxxx
*@return xxxxxx
*@throws xxxxx
*/
其中参数的说明(前置条件)以 @param
作为开头,返回的说明(后置条件)以@return
作为开头,可能抛出的异常以@throws
作为开头。
同时应该注意的是使用Null值很容易发生错误,同时它们也是不安全的,所以在设计程序的时候尽可能避开它们。在这门课程中——事实上在大多数好的Java编程中——一个约定俗成规矩就是参数和返回值不是null。 所以每一个方法都隐式的规定了前置条件中数组或者其他对象不能是null,同时后置条件中的返回对象也不会是null值(除非规格说明显式的说明了可能返回null,不过这通常不是一个好的设计)。
1.2测试与规格说明
在测试中,我们谈到了黑盒测试意味着仅仅通过规格说明构建测试,而白盒测试是通过代码实现来构建测试。但是要特别注意一点:即使是白盒测试也必须遵循规格说明。 你的实现也许很依赖前置条件的满足,否则方法就会有一个未定义的行为。而你的测试是不能依赖这种未定义的行为的。测试用例必须尊徐规格说明,就像每一个客户一样
1.3规格说明的强度
定义:规格说明S2强于(等于)规格说明S1,如果:
- S2的前置条件弱于或等价于S1的
- S2的后置条件强于或等于S1的后置条件
如果S2强于S1,那么任何S2的实现方法都可以拿来实现S1,并且在程序中可以安全的用S2的模块替换S1模块。
在设计时,要权衡好规格说明的强度,既要足够“弱”,这里指前置条件较弱,对客户端的限制较小;也要足够“强”,这里指后置条件要足够强,作为一个负责任的程序员,除了对满足前置条件的输入返回正确的结果外,对于不满足后置条件的输入,要做出对客户端友好的反应。
2.异常
请看另一篇博客:浅析Java异常处理
3.mutable与immutable
mutable类型:当你尝试修改这种变量的值的时候,你真正修改了这个值本身。例如StringBuilder、StringBuffer等
immutable类型:当你给修改这种变量的值的时候,实际的值是无法修改的(内部实现时使用final修饰成员变量)。如果使用某些方法修改成功了,那并不意味着出现了矛盾,因为实际上修改成功是因为方法内部新建了一个对象,然后将修改的值写进这个新变量,再修改原变量的指针,指向新变量。(这也是为什么String是immutable类型,但实际操作时可以修改的原因)例如String,以及所有的基本类型变量及其包装类。是值的不变性。
由于immutable类型比mutable类型安全,所以一般在进行软件构造时,我们提倡使用immutable类型变量。
那如何将自己定义的的变量修改为immutable类型?
一般可以考虑使用flnal关键字进行修饰。但是使用final关键字时,应该注意:
final引起的效果是引用的不变性,所以如果final修饰的是一个基本数据类型的变量(数值型,字符型,布尔型),由于这些变量的值本身就无法修改,加之引用不变性,这个值就变成常量无法修改了;而如果修饰的是一个引用变量(类,接口,数组),那么指向该变量的指针就无法修改了,但是该指针所指向的那个对象还是可以变的,就像你记住了人家的门牌号,但你不能管人家家里人员数量。
(另外:java中的全局变量是由public修饰的static成员变量)