上周末,麦斯博在上海召开了亚太软件研发团队管理年会,我作为讲师参与了架构分会场的演讲。我的演讲题目正是《对象设计的艺术》。“艺术”这个词语有些大,有点玄,不过我确乎希望能将设计作为一种艺术,与工程结合,既注重实效,又能保证软件的质量,代码的优雅。在这次演讲中,我希望能够深层次地挖掘所谓设计的本质。这是我的有感而发。因为在设计领域中,前人已经为我们总结了太多的思想、原则与模式。这些内容汗牛充栋,很多程序员根本无法穷尽其内容。学得越多,感觉懂得越少。而如果就这样无知下去,自然也不利于技能的提升。因此,我尝试着去抓住设计的某些核心价值,这就是我总结出来的七种“武器”:重用、扩展、分离、变化、简约、一致、间接。
重用
软件开发的最大敌人就是重复。它会导致重复开发、无法有效复用以及解决方案蔓延。避免重复的方法包括:保持对象的细粒度、高内聚以及对对象的合理封装。我们可以从方法级、类级以及模块级提高软件的复用性。例如,我们提取方法或类,定义辅助类,按照依赖关系划分模块。如下的类图就是在JUnit Framework中利用模板方法模式实现部分逻辑的复用:
扩展
优良的软件结构可以很好地支持扩展,而不用修改源代码。对于扩展性而言,代表两重含义。其一是内部的扩展,它不会在外部接口上增加新的功能,而仅仅是对对象职责的装饰,或通过代理对象对其进行控制。其二则是外部扩展,我们可以利用继承和组合在重用的基础上,完成对象功能的扩展。当然,最重要的方法是利用抽象。例如,Java提供的Runnable接口,可以有效地支持多线程编程中对业务的扩展:
class MyThreadStart implements Runnable {
public void run() {
//do something
}
}
Thread controller = new Thread(new ThreadStart());
controller.start();
分离
在构建架构时,最重要的一个设计原则就是关注点分离。经典的架构模式例如分层模式与MVC模式正是关注点分离的体现。分离的关键元素是分离变与不变,其中的核心价值即SRP(单一职责原则)。同时,我们在分离对象之后,还要考虑它们之间的协作。下图展示了我对分离的观点:
变化
在软件开发中,变化是不可避免的。在分析需求时,我们必须寻找变化点。根据我的经验,这些功能点经常会发生变化:
1、业务规则(解决方案:规则模式)
2、算法策略(解决方案:策略模式)
3、命令请求(解决方案:命令模式)
4、硬件支持(解决方案:入口模式)
5、协议标准(解决方案:元数据)
6、数据格式(解决方案:数据封装)
7、业务流程(解决方案:工作流定制)
8、系统配置(解决方案:元数据、数据库)
9、界面表现(解决方案:分层模式、MVC模式)
10、外界服务(解决方案:服务外观)
简约
保持软件的简约,需要谨记两个原则:KISS(保持软件的简单与易用)和YAGNI(只实现实际需要的功能,而不要想当然地添加功能)。如何才能简化复杂的实现呢?利用封装可以隐藏复杂的实现,利用抽象可以统一模型,从而消除功能的不同。作为一名架构师,总是希望追求完美的解决方案,这是错误的。许多反模式真是来源于此,例如分析瘫痪,意外的复杂度,以及货运崇拜(在不理解的情况下使用模式)。
一致
所谓“一致”包括接口、形式、调用与解决方案的一致。接口一致,则实现就可以替换;形式一致,则可以窥一斑而知全豹;调用一致,则客户端可以透明访问;而一致的解决方案,则是团队合作的基石。例如,我们可以通过使用合成模式,实现调用的一致:
间接
David Wheeler说过:“计算机科学中的大多数问题都可以通过增加一层间接性来解决。”诚哉斯言。在软件开发中,间接可以通过委托、抽象、协作来体现。间接可以降低依赖,隐藏细节,简化客户端调用。许多模式都体现了间接的思想,例如门面模式、调停者模式、适配器模式、策略模式以及服务定位器模式。