SDWest2006(译注1)对我来说是个有趣的大会。我除了星期三之外(当时我正飞往费城参加一个客户会议 == 因此错过了Jolt颁奖部分)每天都在演讲。我也参加了一些谈话和会议;其中最引人关注的是Mike Cohn的计划与估算的谈话。我的两个谈话都是半天的关于Ood原则的导引。这些谈话都参与的非常好,现场反映也很热烈。
这里是我谈话的几份演讲稿:
- 类设计之高级原则 在OO设计中,掌控类间组合或是关联关系的原则到底是什么?
- 组件设计之高级原则 在大型OO设计中,你该如何掌控组件的组合或关联关系?
- 首要导向因素 专业与否的最低界限是什么?
在星期二我主导了一个关于“敏捷与架构设计”的圆桌会议。很多人是挤着进入会议现场的,而且一直参与到会议结束。配合着讨论要点的导航图,我们将这些要点一一讨论过。我的好朋友John Kern也在那儿,还帮助解答了很多提问。你可以从这里了解更多有关这个会议的。
会议的一些关键要点如下:
- 架构设计的主要目标是灵活性,可维护性和可扩展性。
- 但我们认识到,由测试驱动开发原则指导产生的单元测试和验收测试(译注2)要比灵活性,可维护性和可扩展性来得更重要。
- 因而测试才是首要的影响力,而架构设计只是第二位的。
在此次会议前,我从未想到过这点。这里就有一群架构师和设计师,他们激烈的争辩着架构设计的角色和位置,可是我们辛苦争论出的一致结论是灵活性,可维护性和可扩展性是处于次要的影响作用的,是编写测试(测试优先于产品代码)起着最主要的效用。
这好像是吞下了苦黄莲,而且很多架构设计师们搞不好立即就会拒绝。然而,没人认为架构师不重要,或者认为应该丢掉它转而去青睐测试。正相反,是测试让我们无所顾虑的改进系统的设计。不是测试胜于架构设计;而应该说是测试成全了它!
自动化测试给了我们一个可靠的方式去了解系统是否运行良好。因此我们不必再惧怕一个设计的改变会破坏它。这让实施那些能够改进系统的设计变得更容易。因此测试的粉末登台意味着在持续改进系统的结构、设计和架构时遇到的阻碍会来得更少。
失去测试,架构设计就是天方夜谭,因为一但重大的开发启动后设计将很难改变。而自动化测试给设计留下了空间。设计改变所带来的风险如此之大的被削减,以至于我们可以在肆无忌惮中就让系统进化。
译注:
1,SDWest,全称Software Development West,即西部开发者大会,是全美颇受瞩目的开发者大会,著名的Jolt大奖就在每年一度的大会上颁发。
2,验收测试,原文acceptance test,又成为容忍测试,敏捷方法认为,如果一个构建(build)通过了验收测试,基本上这个构建就可被“接受了”。
(原文链接网址:http://www.butunclebob.com/ArticleS.UncleBob.ArchitectureIsaSecondaryEffect; Robert C. Martin的英文blog网址: http://www.butunclebob.com/ArticleS.UncleBob)
译者注:Robert C. Martin是Object Mentor公司总裁,面向对象设计、模式、UML、敏捷方法学和极限编程领域内的资深顾问。他不仅是Jolt获奖图书《敏捷软件开发:原则、模式与实践》(中文版)(《敏捷软件开发》(英文影印版))的作者,还是畅销书Designing Object-Oriented C++ Applications Using the Booch Method的作者。Martin是Pattern Languages of Program Design 3和More C++ Gems的主编,并与James Newkirk合著了XP in Practice。他是国际程序员大会上著名的发言人,并在C++ Report杂志担任过4年的编辑。
架构设计之性能设计经验
性能(performance)设计非常重要,对于服务器端实时交易系统来说系统性能的重要性不言而喻,对客户端软件来说性能好的软件也会获得良好的用户体验,从而给用户留下高质量软件的良好印象。因此在进行架构设计中性能设计非常重要。
但架构设计实际是一个平衡设计,在可用性、可扩展性、可维护性、可靠性、高性能等之间做个妥协选择。这些非功能性的需求再加上复杂的功能性需求,同时还要考虑到项目管理上tight schedule, low cost, perfect effect的三角难题约束,有时需求还不是很明确,vision不是很清楚,这种情况下系统架构设计真是一门艺术。
单就性能设计来说,在架构设计初期就一定要把系统性能考虑在内,否则等开发完成以后测试发现性能不好就比较难办,通常要花费较长的时间来诊断性能瓶颈,找到提升的办法,甚至要改变架构,伤筋动骨,往往造成项目延期。所以性能设计首先要有明确的性能目标,根据用户和软件本身的性能要求来设计,合适的就是最好的。其次,要有适当的度量标准和量化的性能指标。最后,要有相应的设计策略,具体的测试方法。
根据我的经验,影响系统性能主要瓶颈在I/O,包括数据库,socket,网络通信,文件等,例如频繁查询数据库并返回大量结果集,频繁操作大文件等,这些昂贵的操作会占用大量的CPU时间。拿系统响应和服务一个事务来说,有几个Round trip,要通过哪几层I/O,如何合理的分配这些I/O的调用,降低不必要的I/O,都是进行系统性能设计要考虑的。而有些性能问题在初期并不会表现出来,但当拿到实际上线环境下,存在多用户并发、大数据量的情况下就会暴露出严重的问题。所以性能设计时一定要考虑到I/O,同步,并发,资源争用,以及大数据量等因素。通常,I/O操作、网络响应、差的算法、数据库、以及其他的低效的资源使用都会导致低劣的性能。
具体可用的设计策略有:
- 缓存以及缓存层(caching layer)
在数据层和应用层之间增加数据缓存层,提供全局数据服务。可以大大减少数据库往返次数。与读取数据库和读取大文件(如XML文件)比,读取内存的速度无疑要快的多。所以对经常要访问的数据进行缓存是非常好的实践方法。因为现在系统往往内存很大,可以充分利用大内存,而共享内存更能实现数据并发访问。
- 多线程(multi-threading)
现在基本上大部分软件实现多线程或多进程,多线程对单CPU系统还只是顺序利用CPU时间和改善用户体验,多CPU系统才是真正的并行。要注意的是多线程不要争抢访问同一资源而导致部分串行操作,要做到真正的并行操作多线程并不容易。另外,在多线程间同步一个庞大的资源,过多创建线程又没有实现线程池也会导致系统性能下降。
- 负载平衡(load balancing)
物理上增加地位对等的集群服务器(Cluster),通过负载分配算法分配相应服务器来相应客户端请求。很多系统支持负载均衡,Windows server2003 IIS就支持负载均衡服务,其他如WebLogic, WebSphere也有集群版本支持负载均衡。当然你也可以自己实现负载分配算法。
- 数据库优化(database optimization)
如果应用程序使用了数据库,可以采取许多步骤来消除访问和写入数据时的瓶颈:
- 标识潜在的索引,但不要创建过多的索引。
- 如果使用 SQL Server,则使用 SQL Server 的事件探查器和索引优化向导。
- 监视处理器的使用;理想范围是:75-80% 处理器时间。
- 使用查询分析器分析查询计划以优化查询。
- 使用存储过程优化性能。
- 标准化写入的大量数据 —写入较少的数据。
- 取消标准化读取的大量数据 —读取较少的数据。
文件系统优化
有时候系统性能不好,但当你关闭写log的功能,性能一下子提高很多。因为频繁的打开关闭大log文件时I/O开销非常大,同样记录log到数据库也一样。所以,release版尽量减少写log,或干脆移到裸设备上。
频繁打开关闭文件对系统性能下降程度是惊人的,可以通过一些变通办法来减少文件的频繁操作。
例如,原来的缓存持久化实现是保存在XML文件,每次要获得一个配置项,都打开XML文件,通过XPath拿到这个配置项的值,这样效率不高,而且容易把这个XML文件lock住;改进的方法是:通过比较XML文件的修改时间(System.IO.File.GetLastWriteTime)判断是否要再次打开文件,大大提高了效率;另一个可以改进的方法是:启动时读取所有配置到一个静态的HashTable,每次要获得一个配置项都从内存HashTable获取,在最后或适当的时候持久化到XML。
代码性能设计
在编程实现上,代码性能设计也很重要,一些昂贵的操作会占用大量的资源和CPU时间。例如,字符串相加没用StringBuilder, 频繁创建对象,差劲的排序或递归算法,过多的装箱拆箱,过多的使用反射(Reflection),频繁new HashTable或大的数组,用异常(Catch Exception)用做正常的逻辑,使用复杂的正则表达式,等等。具体可以参考《Effective C++》《Effective C#》等书籍。
语言的选择
另外,语言选择也很重要。比如相对于Java, C#, C++, 大多数OLTP系统用C语言效率高的多,因为在所有的高级程序设计语言中,C程序设计语言的运行效率是公认的。再比如我们熟悉的一些框架,框架本身是C#或是Java的,但其核心独立模块是C++封装的,这样可以达到最佳的性能。所以对于一些特定的业务需求目标和数据的具体情况,对于核心的模块或算法,可以用特定的语言来实现以获得更好的效率。
应用层
比如应用层和数据库的API,在.Net中就有就有DataReader、DataSet和IList等的选择以及转换等,这个根据具体情况而定;还有就是大家常采用的数据的格式化和压缩,以及采用分页,减少传输的数据量;是否可以把一部分处理逻辑放在客户端呢,减少服务端的工作量。界面端也是有很多针对性能优化的考虑,例如绘图,控件重绘都是非常耗资源的,各控件的数据加载和数据绑定性能也各不相同,尽量采用惰性加载,异步加载;初始化和启动速度等都是需要考虑和优化的。
架构设计的三个维度
架构设计是一个非常大的话题,不管写几篇文章,接触到的始终只是冰山一角,更多的是实践中去体会。
这篇文章主要介绍的是面向对象OO,面向方面AOP,面向服务SOA这三个要素在架构设计中的位置与作用。
一、架构设计三个维度
架构设计有三个维度,或者说是我们在考虑架构时需要思考的三个方向。分别为:面向对象、面向方面、面向服务。这三个维度可以看作是正交的,但不同维度会互相印证,互相支撑。
整个架构的示意图如下所示:
二、面向对象
面向对象技术最初是从面向对象的程序设计开始的,它的出现以60年代simula语言为标志,并在Smalltalk语言的完善和标准化过程中得到更多的扩展和对以前的思想的重新注解。80年代中后期,面向对象程序设计逐渐成熟,被计算机界理解和接受,人们又开始进一步考虑面向对象的开发问题。直到现在,面向对象已经成为一种非常流行的编程方式,以及软件设计的架构。
面向对象提出有三个主要目标:重用性、灵活性和扩展性,强调对象的“抽象”、“封装”、“继承”、“多态”。它能让人们以更加接近于现实世界的方式来思考程序,这点可以说是面向对象最大的进步。
在OO思想的运用上,业界出现了很多好的经验与技巧,从而涌现出大量的设计模式。可以说面向对象是系统分析与设计时的一个很重要的方面。
三、面向方面
面向方面最初来源于hook技术,本质上就是满足扩展的需求,可以在程序中自由扩展功能。
面向方面不仅仅是一门编程技术,同样也是一种架构设计的思路。如果说OO是纵向地分析、切割整个系统,那么可以认为AOP是横向地对系统作切片。简单地理解,OO与AOP分别从两个不同的角度给我们提供了分析系统的思路。面向方面可以弥补面向对象的缺陷,两种方式有机的结合在一起可以更加有效地分析系统。
我们认为OO是接近于人类认识自然的思维方式,但对于东方来说却并不是这样。当西方人看到一个复杂系统的时候,只会有一种思路,就是“分解”——将系统分解成一块一块,然后每个部分作研究。当东方人看到一个复杂系统的时候,更多地会关注系统中存在的关系,将系统作为一个有机的整体进行研究。
这两种思维方式都没有问题,结合起来的话分析问题解决问题会更好。面向对象与面向方面也同样如此,都能对应到人类认识自然的思维方式上。不过中国人理解AOP可能会有不同的感悟——我写的文章《读易[13]·闲谈中医与AOP》有简单的说明。
四、面向服务
面向服务可以说是最近炒得比较火的概念了。包括现在提到的SaaS——Software as a service,软件即服务。准确说来,面向服务不仅仅是软件行业的概念。这个要从社会的产业结构说起。
社会产业总共分为三个,第一产业农业,第二产业工业,第三产业服务业。最早社会的主要产业是第一产业农业,将近有几万年的历史。十八世纪下半叶在英国开始的工业革命,对人们的生活产生了根本性的影响,社会的主要产业成了第二产业工业。
现在仍然属于工业时代,或者有人说的“后工业时代”。而在后工业时代,社会的经济体制必定要向第三产业服务业逐渐转型。面向服务其实是社会经济体制重心的一种迁移。
还是说回到软件行业,社会的主要产业将转变成服务业,自然软件行业也会出现对应的变化,那就是这里提到的面向服务。面向服务今后会影响到软件的交付模式,会对整个软件行业的体制产生影响。
而说到架构层面,面向服务是系统发布功能的一种方式。并且基于这种方式下不同的系统之间能有效地通信、协作。常见的实现技术就是Web Service。
五、软件全局观
软件架构设计的三个维度:面向对象、面向方面、面向服务。
最年长的一个维度就是面向对象,发展了好几十年,也是相对来说比较成熟的一个维度。它解决的问题是系统内部结构的设计。
面向方面的思想提出来能够弥补面向对象的缺陷。面向对象的方式不能实现横切关注点的分离,而面向方面正是为了解决这个问题。面向方面与面向对象一样都是解决系统内部结构的设计。
面向服务更多的是涉及到系统的外部,简单地说就是发布功能。它并不关注系统内部结构的实现,所以说面向服务与面向对象或者面向方面并不冲突。
这三个维度并不是绝对孤立的,它们之间会互相影响、制约。我们在分析架构的时候需要同时考虑到这三个维度的问题。这样有助于我们设计出更加优秀的架构。
软件架构为谁而设计
(节选自《软件架构设计》书稿)
……如此看来,架构师应当为项目相关的不同角色而设计(如图5-2所示):
l 架构师要为客户负责,满足他们的业务目标和约束条件;
l 架构师要为用户负责,使他们关心的功能需求和运行期质量属性得以满足;
l 架构师必须顾及处于协作分工“下游”的开发人员,
l 架构师还必须考虑“周边”的管理人员,为他们进行分工管理、协调控制、评估监控等工作提供清晰的基础。
图5-2 软件架构师为谁而设计
一言以蔽之,软件架构师必须做到内外兼顾、各层并重(如图5-3所示)。只有这样,软件架构才能和它“包含了关于如何构建软件的一些最重要的设计决策”的“地位”相符。
补充三点:
●这个话题我在2006IBM开发者大会的预热课堂上有过演讲,说明了如何运用基于多视图的架构设计方法应对上述问题。
●另外可参考我在IBM DW上发的文章:运用RUP 4+1视图方法进行软件架构设计
●其实,《软件架构设计》一书讲述的具体方法和4+1方法有所不同……例如,明确引入“质量属性分析”活动来为性能、可伸缩性、可重用性、可扩展性等非功能需求制定相应的架构决策。书的第15章专门介绍质量属性分析(例如如何运用“质量-场景-决策”表这种思维工具落实需求、制定设计决策等)。