设计匠艺 | 意图导向编程

2018-03-07 15:38:01 浏览数 (1)

意图体现在编程层面,仍然可以作为设计的导向,是谓“意图导向编程”。这种设计方法实则就是让设计者能够换位思考,站在调用者的角度思考接口。“假如我是调用者,我希望对象提供怎样的接口?”这事实上是驱动我们设计出舒适的接口,让人用起来赏心悦目。

测试驱动开发正是通过编写测试用例让开发人员转换思考的角度。由于要编写测试用例,自然就要从使用的角度去思考。思考的范围是Who(谁),What(做什么),而不是How(怎么做)。这正是测试驱动开发的驱动力所在。它就好像禅宗的大师,让你闭目凝思,忘记眼睛所见的实现世界,转而用心灵去触摸接口的真实本质。因为“用”,而提供接口;调用者关心接口,而非实现。

若能以DSL风格设计接口,设计意图更能如行云流水般呈现。让方法调用变为赏心悦目的类自然语言,仿佛是对领域逻辑的自然描述。

关键在于“导向”,它将意图作为驱动设计的入口。在Essential Skill for the Agile Development书中,作者定义意图导向编程为:“先假设当前这个对象中,已经有了一个理想方法,它可以准确无误地完成你想做的事情,而不是直接盯着每一点要求来编写代码。”这说明它要求设计者针对接口,而非实现细节编程。这是一种Top-Down的设计思路,又或者说是由外及内。

领域驱动设计提出了“统一语言(ubiquitous language)”的概念。通过合理地运用统一语言,既可以帮助设计者编写出符合领域专家习惯的代码接口,又能够让程序员更好地思考业务(即使用的方式而非实现)。显然,定义为relocateTo(Address)方法,其表述力要百倍于changeAddress(Address)。

这说明了命名的重要性。一个好的命名可以更加清晰地体现设计意图,从而改进代码的可读性。

不要将程序的可读性简单视为程序设计的小道。认为诸如方法命名、变量定义、语句组织、任务分解等内容,俱是细枝末节,微不足道。然而,对于一个整体的软件系统而言,既需要宏观的架构决策、设计与指导原则,也必须重视微观的代码细节。正如作文,提纲主旨是文章的根与枝,但一词一句,也需精雕细作,才能立起文章的精气神。所谓“细节决定成败”,软件历史上,有许多影响深远的重大失败,其根由往往是编码细节出现了疏漏。

“代码即架构”!正如小说需要角色来说话一般,软件系统的质量好坏,归根结底还是需要代码来告知。代码的优劣不仅直接决定了软件的质量,还将直接影响软件成本。Yourdon和Constantine在著作Structured Design中认为:软件成本由开发成本与维护成本组成,而往往维护成本要远高于开发成本。这其中付出的主要成本就是由于理解代码和修改代码造成的。好的代码常常是可阅读的,要做到这一点,则近似于一种艺术之美了。

如果我们只看类名EmailListingServer,或许会顾名思义认为它就是显示邮件的服务器。——大谬不然!如果我告诉你其实这个类非常繁忙,既要建立与邮件服务器的连接,侦听接收到的邮件;又要根据配置文件中的名单,将接收到的邮件转发给相关人员。此时,你会怎么想?显然,一个错误的容易引起歧义的命名,甚至比一个词不达意的命名还要糟糕。

那么,我们该怎么命名这个类?哎,我好像找不到准确的词语来表达如此众多的职责!那么,我们为什么不将这些职责分开,因为它其实违背了“单一职责原则(SRP)”。于是,我们从抽象层次的角度去思考职责,分离出“侦听”与“转发”的职责,顺其自然地获得了EmailServerListener与EmailForwarder类。至于邮件的收发,以及读取配置文件中的名单,则属于另一个抽象层次了的职责。这正是Kent Beck所谓的“单一层次抽象原则(SLAP)”。

故而,当我们从一个类的名称无法清晰地了解它究竟承担了什么职责,又或者它传达了错误消息时,就说明设计存在坏味道,混淆了设计者想要表达的意图。命名需三思,正如作文,需要字斟句酌,以求文意传神。命名,同样是软件设计的艺术。

0 人点赞