软件构造设计规约总结
软件构造设计规约总结
function & method
例子:
public static void main(String[] args)
{
STATEMENTS
}
注意
- 参数类型是否匹配的检查在静态检查阶段完成
- 返回值是否匹配的检查在静态检查阶段完成
- 完整的方法应该包括其规约:specification,与实现implementation
Specification
1Documenting
包括代码中数据类型的定义与自己的设计决策
2Specification and contract
没规约,没法分派任务,无法写程序;即使写出来,也不知道对错,表现了程序与用户端之间达成的一致。明确双方的责任,定义正确实现的含义。
需要规约的原因:
- 减少开发者的误解,使得开发者容易定位错误,有助于区分责任
- 客户只需阅读spec即可无需阅读代码便可以理解干了什么
- 规约可以隔离“变化”,客户端不需要知道实现(实现细节交给了实现者),实现者不需要知道如何被使用,以提高编码效率 (E.g., 实现者不需要写代码确保输入的正确性,调用者的责任)
3Behavioral equivalence (行为等价性)
两个函数是否等价要站在用户的角度,不能只看其实现代码,要看其规约
4Specificaion的结构:pre-condition 与 post-condition
前置条件:对客户端的约束,在使用方法时必须满足的条件
后置条件:对开发者的约束,方法结束时必须满足的条件(返回值的类型正确,同时满足effect)
契约:如果前置条件满足了,后置条件必须满足。但是如果前置条件不满足,实现者可以做任何事包括抛出异常与返回特定值(虽然实现者没有义务提醒但是可以通过快速失败(抛出异常)来使bug更容易被找到)
5Java中的规约
java中的规约包括两个部分
-
静态类型声明通过此可以进行静态类型检查
这里需要注意@param与@return中标注的应该是变量名而不应该把其类型透露出去
-
方法前的注释,但是这需要人工判定其是否满足
练习:
错误一首先开始为/**,错误二@param中不应该暴露word的实现方法最后boolean与其相同,错误三@requires与@effects
总结以下:一个规约应该给出的是一个方法的参数与返回值但是其不应该给出方法中的局部变量以及局部字段
对于对输入参数进行改变的方法的规约
除非后置条件中声明过否则方法内部不应该改变输入参数,输入mutable对象会使得规约复杂化
程序中可能会有多个变量指向同一个可变对象(别名),我们无法强迫类的实现体与客户端不保存可变变量的“别名”,我们要靠严格的契约来实现。
要防范这种问题不能只依赖于客户端或者实现者关键在于在规约中将不可变限制住(及与两者的实现无关不管怎么样都无法改变)
测试并核实(Testing and verifying)规约
使用黑盒测试来检测特定的规约,及用一种与实现无关的方式
实现例子:
特别要注意测试时一定要只依赖规约,不能用自己想的一种特定的实现来测试
Design Specification设计规约
规约分类
包括规约的确定性(描述的输出是否是确定的(good)还是含有不确定因素),陈述性(是只描述了输出(good),还是描述了如何计算输出),规约的强度。这些因素用于判断哪个规约更好
比较规约强弱
要注意:
-
必须两个条件同时满足才能比较否则无法比较因为前后置条件间没有关系
-
第二个条件中的same state十分重要因为二者应该在相同输入的条件下进行比较
-
只有S2强于S1时才可以用S2替换S1
举个例子:
我们需要知道这二者需要在相同输入域的条件下进行比较因此我们应该选上面的哪个比较强的前置条件带来的输入域
-
记住要求更少做的更多
几个例子:
要注意这个是没法比的在输入更弱的条件下后置条件也更弱了
注意更强的规约意味着可以实现其的方法更少同时也意味着更多的客户可以使用它
规约图
规约图中的点代表了某个实现,规约用椭圆表示,符合规约则落在范围内否则落在范围外。
注意更强的规约在图中表现为更小的面积因为其Implementation中的方法数变少了
设计好的规约的方法
一个好的“方法”设计,并不是你的代码写的多么好,而是你对该方法的spec设计的如何。一方面:client用着舒服 ,另一方面:开发者编着舒服。及满足以下几个条件:
-
规约应该为内聚的及简单单一不要实现过多的功能,反例:
-
信息丰富的。给出更多的关于实现结果的信息,不让客户端产生歧义:
例如以下的方法如果其返回null,我无法确定之前Key是存在的value为null,还是key不存在
-
规约应该够强,太弱的spec,client不放心、不敢用 (因为没有给出足够的承诺)。 开发者应尽可能考虑各种特殊情况,在post-condition给出处理措施。
在这个例子中我们发现出现Exception时无法知道list1是否已经被改变了
-
规约同时也得足够弱太强的spec,在很多特殊情况下难以达到,给开发者增加了实现的难 度(client当然非常高兴)。
我们如果用上面的方法打开一个没有权限的文件该怎么办,规约过于强
-
在规约中使用抽象类型,给方法的实现体与客户端更大的自由度
这个方法强迫实现者用ArrayList实现而我们如果用LinkedList可以更快的实现同时也不会影响最终效果
-
我们应该衡量是否使用前置条件还是得先检查前置条件
对于客户端来说它们不喜欢太强的 precondition,不满足precondition的输入会导致失败,因此惯用做法是: 不限定太强的precondition,而是在postcondition中抛出异常:输入不合法。(与在pre-post condition哪里提到的相似,尽量通过quickfail来找到错误)
当用户调用方法时直接调用public的方法。因此public的方法应该要适当方松条件,而private的方法说明之前public的方法是正确的我们可以相信前置条件。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理