设计匠艺 | 意图导向编程

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

测试驱动开发正是通过编写测试用例让开发人员转换思考的角度。由于要编写测试用例,自然就要从使用的角度去思考。思考的范围是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)”。

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

原文发布于微信公众号 - 逸言(YiYan_OneWord)

原文发表时间:2014-09-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏c#开发者

C# : row-clickable GridView and get and set gridview rows using JavaScript

Complete C# code: ---------------- using System; using System.ComponentModel; ...

2996
来自专栏C/C++基础

C#获取系统当前时间

ystem.DateTime currentTime=new System.DateTime(); 1.1 取当前年月日时分秒 currentTime=Sy...

1153
来自专栏吴伟祥

Java Calendar 类的时间操作 原

Calendar 的 month 从 0 开始,也就是全年 12 个月由 0 ~ 11 进行表示。

783
来自专栏跟着阿笨一起玩NET

C# Stream 和 byte[] 之间的转换

621
来自专栏xingoo, 一个梦想做发明家的程序员

windows程序设计-第四章 system1.c

/*---------------------------------------------------- SYSMETS1.C -- System M...

23710
来自专栏积累沉淀

Hive2.0.0操作HBase 1.2.1报错解决

首先看错  org.apache.hive.service.cli.HiveSQLException: Failed to open new session: ...

2369
来自专栏跟着阿笨一起玩NET

GB2312转换成UTF-8与utf_8转换成GB2312

1911
来自专栏听雨堂

想修改CSS

      下载了一个“通用”的CSS文件,本来想偷懒的,结果发现有问题,就是它用的颜色是变量定义的,无法识别。我又找不到在哪里可以定义。 BODY{     ...

20410
来自专栏码匠的流水账

聊聊spring cloud的LoadBalancerAutoConfiguration

本文主要研究一下spring cloud的LoadBalancerAutoConfiguration

1052
来自专栏闻道于事

商城项目整理(三)JDBC增删改查

商品表的增加,修改,删除,订单表的增加,确认,用户表的查看,日志表的增加,查看 商品表建表语句: 1 create table TEST.GOODS_TABL...

5485

扫码关注云+社区