契约式设计契约式编码是一种设计计算机软件的方法。这种方法描述了,软件设计者应该为软件组件定义正式的、精确的、可验证的接口规范,这样就可以扩展一般抽象数据类型的定义。这些规范称作“契约”,是一个概念上的隐喻,类似于商业契约的条件和业务。
(注:软件组件的概念比较广泛,不要想得过于狭隘)
Design by Contract (DbC) or Programming by Contract is an approach to designing computer software. It prescribes that software designers should define formal, precise and verifiable interface specifications for software components, which extend the ordinary definition of abstract data types with preconditions, postconditions and invariants. These specifications are referred to as "contracts", in accordance with a conceptual metaphor with the conditions and obligations of business contracts. |
因为,契约式设计是美国Eiffel Software的注册商标,所以很多开发人员用“Programming by Contract”,“Contract Programming”或者“Contract-First development”来代替这个词。
Because Design by Contract is a registered trademark of Eiffel Software in the United States, many developers refer to it as Programming by Contract, Contract Programming, or Contract-First development. |
历史History
略。
The term was coined by Bertrand Meyer in connection with his design of the Eiffel programming language and first described in various articles starting in 1986 and the two successive editions (1988, 1997) of his book Object-Oriented Software Construction. Eiffel Software applied for trademark registration for Design by Contract in December 2003, and it was granted in December 2004. The current owner of this trademark is Eiffel Software. Design by Contract has its roots in work on formal verification, formal specification and Hoare logic. The original contributions include: l A clear metaphor to guide the design process l The application to inheritance, in particular a formalism for redefinition and dynamic binding l The application to exception handling l The connection with automatic software documentation |
描述Description
契约式设计的核心思想是基于相互的“义务”与“权利”,对一个软件系统的各个元素之间如何协作一个比喻。这个比喻是从商业活动中“客户”与“供应商”之间达成的“契约”而来。例如:
l 供应商必须提供某种产品(义务),并且有权期望客户付款(权利)。
l 客户必须付款(义务),并且有权得到产品(权利)。
l 双方必须履行一定的义务,它适用于所有的契约,如法律和规定。
The central idea of DbC is a metaphor on how elements of a software system collaborate with each other, on the basis of mutual obligations and benefits. The metaphor comes from business life, where a "client" and a "supplier" agree on a "contract" which defines for example that: l The supplier must provide a certain product (obligation) and is entitled to expect that the client has paid its fee (benefit). l The client must pay the fee (obligation) and is entitled to get the product (benefit). l Both parties must satisfy certain obligations, such as laws and regulations, applying to all contracts. |
同样地,如果面向对象程序设计中的一个类提供了某种功能,那么它要:
l 期望所有调用它的客户模块都保证一定的进入条件:这就是函数的先验条件—客户的义务和供应商的权利,这样它就不用去处理不满足先验条件的情况。
l 保证退出时给出特定的属性:这就是函数的后验条件——供应商的义务,显然也是客户的权利。
l 在进入时假定,并在退出时保持一些特定的属性:不变式。
Similarly, if a routine from a class in object-oriented programming provides a certain functionality, it may: l Expect a certain condition to be guaranteed on entry by any client module that calls it: the routine's precondition—an obligation for the client, and a benefit for the supplier (the routine itself), as it frees it from having to handle cases outside of the precondition. l Guarantee a certain property on exit: the routine's postconditions—an obligation for the supplier, and obviously a benefit (the main benefit of calling the routine) for the client. l Maintain a certain property, assumed on entry and guaranteed on exit: the class invariant. |
契约就是这些义务和权利的正式形式。可以用“三个问题”来概括契约式设计,也是设计者要经常问的:
l 期望的是什么?
l 保证的是什么?
l 保持的是什么?
The contract is the formalization of these obligations and benefits. One could summarize design by contract by the "three questions" that the designer must repeatedly ask: l What does it expect? l What does it guarantee? l What does it maintain? |
很多语言都有对这种断言的支持。然而,契约式设计认为契约对于软件的正确性至关重要,它们应当是设计过程的一部分。实际上,契约式设计提倡首先写断言。
Many languages have facilities to make assertions like these. However, DbC considers these contracts to be so crucial to software correctness that they should be part of the design process. In effect, DbC advocates writing the assertions first. |
契约的概念可以扩展到方法/过程。每个方法的契约通常包含如下信息:
l 可接受和不可接受的值或类型,以及它们的含义
l 返回的值或类型,以及它们的含义
l 可能发生的错误和异常的值或类型,以及它们的含义
l 副作用
l 先验条件
l 后验条件
l 不变量
l (不太常见) 性能保证,如所用的时间和空间
The notion of a contract extends down to the method/procedure level; the contract for each method will normally contain the following pieces of information: l Acceptable and unacceptable input values or types, and their meanings l Return values or types, and their meanings l Error and exception conditions values or types, that can occur, and their meanings l Side effects l Preconditions l Postconditions l Invariants l (more rarely) Performance guarantees, e.g. for time or space used |
派生类被允许弱化先验条件(不能加强),加强后验条件和不变量(不能弱化)。这些规则很接进Liskov替换原则(behavioral subtyping的一种特殊形式)。
Subclasses in an inheritance hierarchy are allowed to weaken preconditions (but not strengthen them) and strengthen postconditions and invariants (but not weaken them). These rules approximate behavioral subtyping. |
所有类之间的关系就是客户类与供应商类之间的关系。客户可以调用供应商的特性,不违反供应商的状态。接下来,供应商也有义务向客户提供不违背客户需要的状态和数据。例如,一个供应商数据缓冲区要求,当调用删除特性时,数据是存在的;接下来,供应商向客户保证,当删除完成时,数据项会被删除。
例如,供应商的delete功能要求客户在data buffer当中有数据存在。相应的,供应商要保证当delete功能完成后,data buffer中的数据已被删除。其他的设计契约还有不变式。不变式保证,在执行完每个功能后,类的状态被都保持在一个规定的状态。
All class relationships are between Client classes and Supplier classes. A Client class is obliged to make calls to Supplier features where the resulting state of the Supplier is not violated by the Client call. Subsequently, the Supplier is obliged to provide a return state and data that does not violate the state requirements of the Client. For instance, a Supplier data buffer may require that data is present in the buffer when a delete feature is called. Subsequently, the Supplier guarantees to the client that when a delete feature finishes its work, the data item will, indeed, be deleted from the buffer. Other Design Contracts are concepts of "Class Invariant". The Class Invariant guarantees (for the local class) that the state of the class will be maintained within specified tolerances at the end of each feature execution. |
当使用契约时,供应商不用去验证是否满足契约的条件。通常的看法是,利用契约条件校验为保护网,在契约被违反的情况下代码要“严重失败”。契约式设计的“严重失败”性质把契约行为的调试简化为每个过程的意图被清晰地规定。这与防御性编程有明显的区别,在防御性编程中,当先验条件被破坏时,供应商负责如何解决。更多的情况是,在契约式设计和防御性编程中,当先验条件被破坏时,供应商抛出一个异常信息给客户,客户必须做出反应。契约式编程使得供应商的工作更简单了。
(注:比如,项目中,当我们认为某段代码可能会出错时,会加入try{}catch{}finally{},让程序常常返回NULL,并记录错误日志。)
When using contracts, a supplier should not try to verify that the contract conditions are satisfied; the general idea is that code should "fail hard", with contract verification being the safety net. DbC's "fail hard" property simplifies the debugging of contract behavior as the intended behavior of each routine is clearly specified. This distinguishes it markedly from a related practice known as defensive programming, where the supplier is responsible for figuring out what to do when a precondition is broken. More often than not, the supplier throws an exception to inform the client that the precondition has been broken, and in both cases—DbC and defensive programming—the client must figure out how to respond to that. DbC makes the supplier's job easier. |
契约式设计同时也定义了软件模块正确性的标准:
l 当客户调用供应商前,如果不变式和先验条件为真,那么在服务(调用)完成后,不变式和后验条件将也为真。
l 当调用供应商时,软件模块应不违反供应商的先验条件。
Design By Contract also defines criteria for correctness for a software module: l If the class invariant AND precondition are true before a supplier is called by a client, then the invariant AND the postconditions will be true after the service has been completed. l When making calls to a Supplier, a software module should not violate the Supplier's preconditions. |
因为,契约条件在程序运行期间不应被违反,出于性能的考虑,它们只可以作为调试代码而被保留,或者在发布版本中被移除。
Because the contract conditions should never be violated in program execution, they can be either left in as debugging code or removed from the production version of the code altogether for performance reasons. |
契约式编程也可以用来代码重用,因为每段代码的契约都被完整地文档化了。模块的契约可以被当作软件文档一种形式。
Design by Contract can also facilitate code reuse, since the contract for each piece of code is fully documented. The contracts for a module can be regarded as a form of software documentation for the behavior of that module. |
Relationship with software testing
单元测试在一个隔离的环境下测试一个模块,来检查它是否符合它的契约。集成测试检查各个模块是否可以很好的协同工作。
Unit testing tests a module in isolation, to check that it meets its contract assuming its subcontractors meet theirs. Integration testing checks whether the various modules are working properly together. |
本文介绍了契约式设计的基本概念,包括其历史背景、核心思想及如何应用于软件开发过程中。此外,还探讨了契约式设计与单元测试、集成测试的关系。
2195

被折叠的 条评论
为什么被折叠?



