契约式设计揭秘
给那些觉得自己的编码风格缺少了点什么的人——接口。
引言
契约式设计(Design by Contract)是一种源自基于接口编程的编码技术或方法论。直观地说,如果你哪天不小心创建了一个对象,它的部分行为符合某个接口,并且你用一个接口引用来实例化这个对象,那么你就在运用一种称为契约式设计的技术。
这项技术的创造者通过观察承包商(企业)和客户(最终用户)之间通常发生的合同和正式协议,获得了这项技术。双方在签署合同后,都必须遵守合同中的规则,包括预定的交货日期、支持项目需求等等。创造者们注意到,合同通常规定了产品供应商在“知道什么”(Know-How)和“怎么做”(How-To)方面的规定,并且只关注双方在支持和交付产品方面的沟通。这种技术同样适用于我们的编程世界。
因此,契约式设计的主要思想是,一个类及其客户端之间存在一个契约:客户端在调用类的方法之前必须保证某些条件成立;类在调用之后保证某些属性成立。
应用契约式设计
当人们认为契约式设计是属性(Attributes)和接口(Interfaces)的结合时,就会产生一种误解,就像我在同一网站上的一篇文章中读到的那样。契约式设计首先是一种在设计应用程序或模式时,在抽象层或高级层使用的技术。正确使用时,接口(意图)和实现接口的类(实现)会产生一种策略,该策略禁止(隐藏)类的细节(特定行为)。
契约式设计是组件开发的核心。大多数现成可用(COTS)的组件都严重依赖于将意图与实现分离,从而为组件用户提供公共接口来访问它。
接口大量使用多态性,允许调用具有相似行为的异构对象;在设计复杂对象时,这是一项非常重要的技术。
由于.NET中的多重继承禁止继承自多个类,因此接口受到了欢迎。我在这里要补充的一点是,如果你继承自多个接口但没有相应的策略,我觉得这没什么用。就像合同中的一条规则,一条规则的意图是为了支持另一条规则的意图。因此,多重接口继承背后的逻辑是为了在类中包含不同的特定行为。
有时与契约式设计无关但在此值得一提的另一个方面:接口在组合(Composition)中扮演着重要角色(组合是通过将工作委托给另一个合适的对象来扩展职责)。如果设计得当,接口在这方面就成为了契约式设计。
永远记住,接口是一种特殊的引用类型:你可以迭代接口,返回接口,可以与参数一起使用它们,可以声明接口的数组,等等……
何时使用契约式设计
说得清楚一点,契约式设计被广泛使用;它几乎在软件工程领域的任何地方都被使用。每当你想到可维护性(Maintainability)、可重用性(Re-use,接口重用)、灵活性(Flexibility)、安全性(Security,这需要另一个话题或讨论)、最大容错性(Maximum fault-tolerance)、组件间通信(Communication between components)以及实现隐藏(implementation hiding)时,就存在对契约式设计的需求。
- 可维护性:在基于组件的软件工程(CBSE)中被广泛采用。由于面向组件/基于组件的软件工程(COP/CBSE)在与其他组件通信时依赖于公共接口,所有内部结构都受公共接口的约束,实现对公众隐藏。当需要更改类或方法的内容时,无需更改公共接口,因此客户端代码永远不会改变。
- 可重用性:大多数使用契约式设计开发应用程序(通常是组件)的软件工程师倾向于重用接口而不是类或方法;你们中的许多人可能会对此争论,但当你们考虑他们的策略/技术时,你们可能会重新考虑模仿他们的工作流程。
- 灵活性 = 可维护性 + 可重用性。
- 安全性:我目前正在设计一种利用契约式设计来提高安全性的方法。目前,我正处于在功能层面嵌入安全性的阶段。
- 最大容错性 = 灵活性 + 设计良好(组件间通信) + 实现隐藏。
一些最后的想法
我相信契约式设计处于战略性编程实践的边缘,就像继承一样;它提供了一种机制,如果彻底实施,将加快您的生产速度,消除模糊的设计,此外,它还能保护您的代码,并限制不受欢迎的接口公开使用。
我老板曾经对我说:“吉米,知识属于每个人”,从那时起,时间对我来说就等同于知识。这是我第一次在互联网上发表文章。请理解,我所说的一切并非都绝对正确。我可能在某些地方犯了错误,所以请对您发现的错误提出评论,以便我们能在此基础上建立一个适当的知识库,供他人加速学习。