译文:Design by Contract(契约式设计)

Design by Contract

原文来自 UML Distilled 3rd edition

Design by Contract是Bertrand Meyer总结的一项设计技巧,也是Meyer发明的Eiffel语言的主要特点。不过,这条原则的作用范围并不局限于Eiffel,而是所有的程序设计语言。

Design by Contract的核心是断言(assertion)。所谓“ 断言”,是指永远为真的布尔型语句,如果不为真,则程序必然存在错误。通常情况下,检查断言的时机,应该局限于调试(debug)阶段,而不是代码的实际执行阶段。实际上,完成的程序永远不应期望断言会被检查。

Design by Contract使用了三类断言:后继条件(post-conditions),前提条件(pre-conditions),以及不变量(invariants)。前驱条件与后继条件都是针对操作(operation)而言的。所谓“ 后继条件”,是指操作执行完之后的情况。举例来说,如果我们定义对某个数的“求平方根”操作,则该操作的后继条件为:input = result * result,这里的result是输出结果,而input是输入的数值。在描述“做什么”(what we do)而不涉及“怎样做”(how we do it)——换言之,将接口和实现分离开来——时,后继条件是非常有用的方法。

所谓“ 前提条件”,是指在执行操作之前,期望具备的环境。对“求平方根”操作来说,前提条件可以定义为input >= 0。根据该前提条件,对某个负数执行“求平方根”操作本身就是错误的,而且结果无可预料。

初看起来,这一切显得乱糟糟的,因为我们必须设置一些检查,来保证“平方根”操作能够被正确执行。然而,重要的问题在于,哪一方负责进行这种检查。

根据前提条件来判断,检查的义务无疑落在调用方(caller)。如果责任划分不明确,则我们或者需要设置太多的检查——每一方都要检查,或者需要太少的检查——每一方都期望对方进行检查。检查太多是很糟糕的,因为它会造成大量重复的代码,增加系统的复杂程度。明确哪一方应该进行检查,能够避免这种问题。调用方或许会忘记进行检查,但我们可以通过调试(debugging)和测试(testing)来降低这种风险。

基于以上对前提条件和后继条件的定义,我们可以得到关于术语“异常”(exception)的严格定义。如果在满足前提条件的情况下调用某操作,不能满足后继条件,这种情况即称为异常。

所谓“ 不变量”(invariant),使关于类(class)的断言。例如,某个帐户类(Account class)可能有不变量表示balance == sun(entries.amount())(也就是说,余额等于所有账目记录的总和)。对于该类的所有实例来说,不变量应该恒为真(always true)。这里说的“恒”,是指“无论是否能对该对象调用某种操作”。

从本质上说,这意味着,不变量可以应用于特定类暴露的所有公开操作的前提条件与后继条件。在方法的执行过程中,不变量可能为假,但是,在其他任何对象能够与被调用方进行交互的时刻,不变量断言必须恢复为真。

在继承关系中,断言扮演着独特的角色。继承的风险之一在于,开发人员为子类重新定义的行为,可能会违背父类的行为。断言减少了这种风险。对某个类来说,其不变量和后继条件必须能够应用于所有的子类。子类可以加强这两类断言(也就是说,增加更多的限制——译者注),而不能削弱它们。而前提条件则只能削弱,而不能增强。

乍看起来,这显得有些古怪,但对于动态绑定(dynamic binding)来说,这是极其重要的。开发人员必须时刻记得,把子类对象作为父类的实例来对待。如果子类对象增强了前提条件,那么调用其父类的方法时,就可能出现错误。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值