介绍:
方法的规约
前置条件 后置条件
欠定规约 非确定规约
陈述式规约 操作式规约
规约的强队及其比较
1.程序中的方法和函数
**“方法”**是程序的“积木”,可以被独立开发、测试、复用使用“方法”的客户端,无需了解方法内部具体如何工作—“抽象”
方法有时需要接受函数,或者返回参数,参数类型是否匹配,在静态类型检查阶段完成,返回值类型是否匹配,也在静态类型检查阶段完成。使用方法的客户端仅仅需要知道参数和返回值
方法中需要包括两部分:规约spec和实现implementation;
2.规约 Specification
2.1规约
规约是团队合作的关键,没有一个统一的规约就无法完成程序
规约也可以是客户端和程序员之间的契约,给“供需双方”都确定了责任,在调用的时候双方都要遵守
很多bug来自于双方之间的误解
不写下来,那么不同开发者的理解就可能不同
没有规约,难以定位错误
精确的规约,有助于区分责任
客户端无需阅读调用函数的代码,只需理解spec即可
例如:
有了规约,则客户端不需要了解程序的实现,只需要知道程序是否按照规约来执行(给出合法结果)即可,规约隔离了客户端和代码,代码变化无需告知客户端,无论怎么变,只要附和规约即可,可以提高代码效率,规约扮演防火墙的角色。
规约只讲“能做什么”,不讲“怎么实现”
▪对象和用户之间的协议
方法签名(类型规格)输入/输出的数据类型
功能和正确性预期功能和正确性
业绩预期性能
2.2行为等价性
同一个方法可以有不同的代码实现,对于客户端来说,如何判断不同的行为是否等价?
由于客户端只知道规约,看不到具体的代码细节,因此根据规约来判断行为是否等价,如果两个函数附和同意规约则他们等价
2.3规约的组成: 前置条件和后置条件
前置条件:对客户端的约束,在使用方法时必须满足的条件,可理解为参数
后置条件:对开发者的约束,方法结束时必须满足的条件,可理解为返回值
契约:如果前置条件满足了,后置条件必须满足
前置条件不满足,则方法可做任何事情:你违约在先,我自然不遵守承诺”
除非在后置条件里声明过,否则方法内部不应该改变输入参数
应尽量遵循此规则,尽量不设计mutating的spec,否则就容易引发bugs
程序员之间应达成的默契:除非spec必须如此,否则不应修改输入参数
尽量避免使用mutable的对象
3.设计规约
规约具有确定性:给定的输入定义了唯一一个可定能的结果
规约具有陈述性:
规约具有强度之分
判断一个规约是否可以替换另一个规约的方法:比较两个spce的强度,更强的spec可以替换更弱的spec
判断强度的方法:跟放松的前置条件+更严格的后置条件。
**更严格的后置条件:**在满足前一个方法的前置条件的基础之上后置条件的变化,如果不满足则任然是不变。
对于不强也不弱的两个spec,无法比较。
越强的规约意味着程序员实现方法的自由度和责任越重,而客户端的责任越轻
确定的规约:给定一个满足前置条件的输入,其输出是唯一的,明确的。
欠定的规约:同一个输入可能有多个输入和输出
非确定的规约:同一个输入多次执行时得到的结果可能不同
程序员可以规约的范围内自由选择实现方式
客户端无需了解具体使用了那个实现
规约图
设计好的spec:
客户端和开发者用着都舒服
功能描述简单,容易理解
不能有歧义
强弱合适:太弱的客户端不放心,太强的程序员任务重
在规约里使用抽象类,给方法的实体和客户端更大的自由度
惯用做法:不限定太强的前置条件,而是在后置条件中抛出异常:输入不合法