软件构造设计规约总结

function & method

例子:

public static void main(String[] args)
{
STATEMENTS
}

注意

  1. 参数类型是否匹配的检查在静态检查阶段完成
  2. 返回值是否匹配的检查在静态检查阶段完成
  3. 完整的方法应该包括其规约:specification,与实现implementation

Specification

1Documenting

包括代码中数据类型的定义与自己的设计决策

2Specification and contract

没规约,没法分派任务,无法写程序;即使写出来,也不知道对错,表现了程序与用户端之间达成的一致。明确双方的责任,定义正确实现的含义。

需要规约的原因:

  1. 减少开发者的误解,使得开发者容易定位错误,有助于区分责任
  2. 客户只需阅读spec即可无需阅读代码便可以理解干了什么
  3. 规约可以隔离“变化”,客户端不需要知道实现(实现细节交给了实现者),实现者不需要知道如何被使用,以提高编码效率 (E.g., 实现者不需要写代码确保输入的正确性,调用者的责任)

image-20220606164430533

3Behavioral equivalence (行为等价性)

两个函数是否等价要站在用户的角度,不能只看其实现代码,要看其规约

 

4Specificaion的结构:pre-condition 与 post-condition

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

后置条件:对开发者的约束,方法结束时必须满足的条件(返回值的类型正确,同时满足effect)

契约:如果前置条件满足了,后置条件必须满足。但是如果前置条件不满足,实现者可以做任何事包括抛出异常与返回特定值(虽然实现者没有义务提醒但是可以通过快速失败(抛出异常)来使bug更容易被找到)

5Java中的规约

java中的规约包括两个部分

  1. 静态类型声明通过此可以进行静态类型检查

    image-20220606171037510

    这里需要注意@param与@return中标注的应该是变量名而不应该把其类型透露出去

    image-20220606171349600

  2. 方法前的注释,但是这需要人工判定其是否满足

练习:

image-20220606171540306

错误一首先开始为/**,错误二@param中不应该暴露word的实现方法最后boolean与其相同,错误三@requires与@effects

总结以下:一个规约应该给出的是一个方法的参数与返回值但是其不应该给出方法中的局部变量以及局部字段

对于对输入参数进行改变的方法的规约

除非后置条件中声明过否则方法内部不应该改变输入参数,输入mutable对象会使得规约复杂化

程序中可能会有多个变量指向同一个可变对象(别名),我们无法强迫类的实现体与客户端不保存可变变量的“别名”,我们要靠严格的契约来实现。

要防范这种问题不能只依赖于客户端或者实现者关键在于在规约中将不可变限制住(及与两者的实现无关不管怎么样都无法改变)

image-20220606195015979

测试并核实(Testing and verifying)规约

使用黑盒测试来检测特定的规约,及用一种与实现无关的方式

实现例子:

image-20220606201019546

特别要注意测试时一定要只依赖规约,不能用自己想的一种特定的实现来测试

Design Specification设计规约

 

规约分类

包括规约的确定性(描述的输出是否是确定的(good)还是含有不确定因素),陈述性(是只描述了输出(good),还是描述了如何计算输出),规约的强度。这些因素用于判断哪个规约更好

比较规约强弱

image-20220606203632793

要注意:

  1. 必须两个条件同时满足才能比较否则无法比较因为前后置条件间没有关系

  2. 第二个条件中的same state十分重要因为二者应该在相同输入的条件下进行比较

  3. 只有S2强于S1时才可以用S2替换S1

    举个例子:

    image-20220606210548359

    我们需要知道这二者需要在相同输入域的条件下进行比较因此我们应该选上面的哪个比较强的前置条件带来的输入域

  4. 记住要求更少做的更多

几个例子:

image-20220606210747487

image-20220606210819325

要注意这个是没法比的在输入更弱的条件下后置条件也更弱了

image-20220606212035516

注意更强的规约意味着可以实现其的方法更少同时也意味着更多的客户可以使用它

规约图

规约图中的点代表了某个实现,规约用椭圆表示,符合规约则落在范围内否则落在范围外。

注意更强的规约在图中表现为更小的面积因为其Implementation中的方法数变少了

image-20220606214431674

image-20220606214609894

设计好的规约的方法

一个好的“方法”设计,并不是你的代码写的多么好,而是你对该方法的spec设计的如何。一方面:client用着舒服 ,另一方面:开发者编着舒服。及满足以下几个条件:

  1. 规约应该为内聚的及简单单一不要实现过多的功能,反例:

    image-20220606231812934

  2. 信息丰富的。给出更多的关于实现结果的信息,不让客户端产生歧义:

    例如以下的方法如果其返回null,我无法确定之前Key是存在的value为null,还是key不存在

    image-20220606231949108

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

    image-20220606232206655

    在这个例子中我们发现出现Exception时无法知道list1是否已经被改变了

  4. 规约同时也得足够弱太强的spec,在很多特殊情况下难以达到,给开发者增加了实现的难 度(client当然非常高兴)。

    image-20220606232344113

    我们如果用上面的方法打开一个没有权限的文件该怎么办,规约过于强

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

    image-20220606232545280

    这个方法强迫实现者用ArrayList实现而我们如果用LinkedList可以更快的实现同时也不会影响最终效果

  6. 我们应该衡量是否使用前置条件还是得先检查前置条件

    对于客户端来说它们不喜欢太强的 precondition,不满足precondition的输入会导致失败,因此惯用做法是: 不限定太强的precondition,而是在postcondition中抛出异常:输入不合法。(与在pre-post condition哪里提到的相似,尽量通过quickfail来找到错误)

    image-20220606233134957

    当用户调用方法时直接调用public的方法。因此public的方法应该要适当方松条件,而private的方法说明之前public的方法是正确的我们可以相信前置条件。

 

  • 29
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kzer111

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值