软件构造课笔记:第5章——设计规约

设计规约

程序设计语言中的函数与方法

参数类型与返回值是否匹配,在静态类型检查阶段完成

“方法”是程序的“积木”,可以被独立开发、测试、复用 使用“方法”的客户端,无需了解方法内部具体如何工作 — “抽象”

规约:编程是为了交流

编程中的记录

如Java语言的API:其记录了类层次、接口列表以及对方法、构造函数的描述等。

代码中的一些操作也是记录:如final、变量的类型体现了设计的决策。

代码本身就蕴含着你的设计决策,但是远远不够,需要用文字体现。

为什么要写出“假设”?第一:自己记不住;第二:别人不懂。

代码蕴含的“设计决策”:给编译器读;注释形式的“设计决策”:给自己和别人读

规范和合同(关于一种方法)

没规约,没法分派任务,无法写程序;即使写出来,也不知道对错

需要程序与客户端之间达成的一致

Spec给“供需双 方”都确定了责任,在调用的时候双方都要遵守

现实情况是:很多bug来自双方之间的误解,不写下来不同开发者的理解就不同,没有规约的情况难以定位错误

优点:精确的规约,有助于区分责任,客户端无需阅读被调用函数的代码,只需理解spec即可

作用

1.规约可以隔离“变化”,无需通知客户端

2.规约也可以提高编码效率,实现者不需要写代码确保输入的正确性,调用者的责任

3.扮演“防火墙”角色,客户端不需要知道实现,实现者不需要知道如何被使用

行为等价性

需要站在客户端看待,如果符合同一规约则等价

规格结构:先决条件和后决条件

前置条件:对客户端的约束,在使用方法时必须满足的条件

前置条件:对客户端的约束,在使用方法时必须满足的条件

异常行为:如果违反了先决条件,它会怎么做

契约:如果前置条件满足了,后置条件必须满足

当前置条件被违反时,说明客户端有bug, 尽管实现者没有义务提醒,但可通过快速失败使bug更容易被找到和修复

如果在调用方法时前置条件不成立,则实现不受后置条件的约束。它可以自由地做任何事情,包括不终止,抛出异常,返回任意结果,进行任意修改,等等。

参数由方法前的注释也是一种规约,但需人工判定其是否满足,如下表示注释的含义:

@param 输入参数的含义

@return 返回参数的含义

@throws 抛出异常的含义

@show 输出的含义

可变方法的规范:

除非在后置条件里声明过,否则方法内部不应该改变输入参数。应尽量遵循此规则,尽量不设计可变的规约,否则就容易引发bug。除非spec必须如此,否则不应修改输入参数。

可变对象可以使简单的规范非常复杂,尽量避免使用Mutable的对象。可变对象降低了可更改性。程序中可能有很多变量指向同一个可变对象(别名),无法强迫类的实现体和客户端不保存可变变量的“别名”。涉及可变对象的契约现在就依赖于每个引用可变对象的人的良好行为,这使得情况变得复杂。

设计规约:

规范分类:1.规约的确定性;2.规约的陈述性;3.规约的强度

如何以判断是否可以用一个规约替换另一个?

1.前置条件更弱或相等 ( 不强于 )

2.后置条件更强或相等 ( 不弱于 )

规约的相对强弱举例:

图示规约

图中每一个点都代表一个规约实现

某个具体实现,若满足规约,则落在其范围内;否则,在其之外。

程序员可以在规约的范围内自由选择实现方式,客户端无需了解具体使用了哪个实现。

更强的规约,表达为更小的区域:

更强的后置条件意味着实现的自由度更低了:在图中的面积更小
更弱的前置条件意味着实现时要处理更多的可能输入,实现的自由度低了:面积更小

设计良好的规约

关于规范的形式:它显然应该简洁,清晰、结构清晰,便于阅读。然而,规范的内容更难规定。
没有万无一失的规则,但有一些有用的指导方针。一个好的“方法”设计,并不是你的代码写的多么好,而是你对该方 法的spec设计的如何。一方面:client用着舒服,另一方面:开发者编着舒服

太弱的spec,client不放心、不敢用(因为没有给出足够的承诺)。 开发者应尽可能考虑各种特殊情况,在post-condition给出处理措施。

太强的spec,在很多特殊情况下难以达到,给开发者增加了实现的难度(client当然非常高兴)。

在规约里使用抽象类型,可以给方法的实现体与客户端更大的自由度

客户端不喜欢太强的 precondition,不满足precondition的输入会导致失败。惯用做法是: 不限定太强的precondition,而是在postcondition中抛出异常:输入不合法。尽可能在错误的根源处fail,避免其大规模扩散

归纳: 是否使用前置条件取决于

(1) check 如果只在类的内部使用该方法 (private) 不需要判断输入是否满足,认为 client 的代价; (2) 方法的使用范围 ,那么可以使用前置条件 会保证前置条件 ) ( 方法内部 ,在使用该方法的各 个位置进行 check —— 责任交给内部 client 如果在其他地方使用该方法 法内部检查输入是否满足 ) (public) ; ,那么可以不使用 ,若 client / 放松前置条件 ( 在方 端不满足则方法抛出异常

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值