敏捷思惟-架构设计中的方法学

<script language='JavaScript'>eLong_Affiliate_MemberID=4053787;</script><script language="javascript" src="http://icons.elong.com/adjs/jd_jp/sw/13.js"></script>
方法论对软件开发而言意味着什么?咱们如何看待软件开发中的方法论?方法论可以成为软件开发的救命稻草吗?在读过此文后,这些疑惑就会获得解答。
 
在第一篇文章中,咱们来了解标题中的一些词的含义。方法学是什么? 敏捷是什么? 为何讨论架构?方法论方法论的英文为Methodology,词典中的解释为"A series of related methods or techniques"咱们能够把它定义为软件开发(针对软件开发)的一整套方法、过程、规则、实践、技术。关于方法论的出现的问题,我很赞同Alistair Cockburn的一句话,"方法论源于恐惧。"出于对项目的超期、成本失控等等因素的恐惧,项目经理们从之前的经验出发,制定出了一些控制、监测项目的方法、技巧。这就是方法论产生的缘由。在Agile Software Development一书中,做者提到了方法论的十三个要素,基本可以函盖方法论的各个方面:角色(Roles) 个性(Personality) 技能(Skills) 团队(Teams) 技术(Techniques) 活动(Activities) 过程(Process) 工件(Work products) 里程碑(Milestones) 标准(Standards) 质量(Quality) 工具(Tools) 团队价值(Team Values)它们之间的关系能够用一幅图来表示:
图 1.
方法论的十三个要素不少的方法论,都涉及了上面列举的十三要素中的部分要素,所以,咱们能够把方法论看做是一个抽象的、无穷的超集,而现实中的方法论都是指超集的一个有限的子集而已。它们之间的关系就好像有理数和1到100之间的整数的关系同样。不管是XP,仍是UI设计经验之类,都属于方法论的一个子集,只是这两个子集之间有大小的差异而已。咱们还应该看到,讨论一个完备的方法论是没有意义的,所以这种方法论铁定不存在,就好像你视图穷举出全部的有理数同样荒唐。所以,咱们关于一个通用的方法论的说法也是无心义的。
 
好的方法论,好比说XP、水晶系列,它们都有一个适合的范围,由于它们了解一点,本身并非一个无所不能的方法论。在现实中,咱们其实不断的在接触方法论。好比说,为了控制项目的进度,项目经理要求全部的开发人员每周递交一份详细的进度报告,这就是一种方法、一种技巧。若是把开发过程当中的这些技巧系统的组织起来,就可以成为一种方法论。你可能会说,那一种方法论的产生也太容易了吧。不,这样产生的方法论并无太大的实用价值,没有实用价值的方法论根本就没有存在的必要。所以,一个成功的方法论是要可以为多个的项目所接受,而且可以成功实现软件的交付的方法论。我和个人同事在实践中作了一些试验,但愿可以把一些好的方法论应用于开发团队。试验的结果很无奈,方法论实施的效果并不理想,一开始咱们认为是方法自己的缘由,到后来,咱们发现事情并非这么简单。在试验的过程当中,开发人员一致认同方法论的优点所在,可是在实施过程当中,鲜有坚持的下来的。
 
在Agile Software Development中,我发现做者遇到了和咱们同样的问题。 Alistair Cockburn在和大量的项目团队的访谈以后,写成了Agile Software Development一书。在访谈以前,他笃定本身将会发现高度精确的过程控制是成功的关键所在,结果他发现事实并不是如此,他把他的发现归结为7条定律。而我在实际中的发现也包含在这七条定律中,总结起来就只有两点:沟通和反馈。只要可以保证良好的沟通和即时的反馈,那么开发团队即便并无采用先进的方法论,同样能够成功。相反,那些"高质量"的团队却每每因为缺少这两个因素而致使失败(咱们这里指的失败是用户拒绝使用最终的软件)。
 
最有效,而成本也最低的沟通方法就是面对面(face to face)的沟通,而随着项目团队的变大,或是另一些影响因素的加入(好比地理位置的隔绝),面对面的沟通愈来愈难实现,这致使沟通的的成本逐渐加大,质量也慢慢降低。但这并非说非面对面的沟通不可,重要的是咱们须要知道不一样的沟通方式的成本和质量并不相同。
 
XP方法尤其强调面对面的沟通,经过现场客户、站立会议、结对编程等方式来保证沟通的有效。在个人经验中,一个开发团队实际上是须要多种沟通方式的结合的。彻底的面对面的沟通对某些团队来讲是很难实现的,那么问题的关键就在于你如何应用沟通的方式来达到你但愿的效果。在前不久结束的欧莱雅创业计划大赛上,有一支团队特别引人注目,他们彼此间素未谋面,仅仅凭借Internet和电话完成了高效的合做。他们虽然没有使用面对面的沟通方式,可是仍然达成了既定的目标。软件开发也是同样的,面对面的沟通是很是有必要的,但其它的沟通方式也是须要的。
 
再看反馈,不管是控制进度,仍是保证客户的满意度,这些活动都须要管理成本。软件开发中的管理成本的一个通性就是伴随有中间产出物(intermediate delivery)。好比说咱们的需求规约、分析文档、设计文档、测试计划,这些都属于中间产出物。中间产出物的增长将会带来效率降低的问题,由于开发人员的时间都花在了完成中间产出物的工做上,花在给软件新功能上的时间就减小了。而中间产出物的主要目的是两个,一个是为了保证软件如客户所愿,例如需求规约;另外一个是为了做为团队中的其余成员工做的输入,例如开发计划、测试计划等。所以,咱们也能够针对这两点来商讨对策,一种是采用迭代的思想,提升软件发布的频率,以保证客户的需求被确实的知足,另外一种就是缩小团队的沟通范围,保证成员可以从其余人那里获得新的思路,而不是撰写规范的内部文档(内部文档指那些仅为内部开发人员之间的沟通所须要的文档)。
 
所以,一个软件项目的成功和你采用的开发方法论并无直接的关系。重量咱们根据把拥有大量artifact(RUP官方翻译为工件,意思是软件开发过程当中的中间产物,如需求规约、设计模型等)和复杂控制的软件开发方法称为重型(Heavy Weight)方法,相对的,咱们称artifact较少的方法为轻型(Light Weight)方法。
 
在传统的观念中,咱们认为重型方法要比轻型安全许多。由于咱们之因此想出重型方法,就是因为在中大型的项目中,项目经理每每远离代码,他没法有效的了解目前的工程的进度、质量、成本等因素。
 
为了克服未知的恐惧感,项目经理制定了大量的中间管理方法,但愿可以控制整个项目,最典型的莫过于要求开发人员频繁的递交各类表示项目目前状态的报告。
 
在Planning XP一书中有一段讨论轻重型方法论的精辟论述,它把重型方法论归结为一种防护性的姿态(defensive posture),而把轻型方法论归结为一种渴望成功(Plan to win)的心态。若是你是采用了防护性姿态,那么你的工做就集中在防止和跟踪错误上,大量的工做流程的制定,是为了保证项目不犯错误,而不是项目成功。而这种方法也不可谓很差,但前提是若是整个团队可以知足前面所提到的两个条件的话,项目也确定会成功,可是重型方法论的一个弊端就在于,你们都在防止错误,都在害怕错误,所以人和人之间的关系是很微妙的,要达到充分的沟通也是很难的。最终,连对人的评价也变成是以免错误的多寡做为考评的依据,而不是成就。
 
咱们在作试验的时候,一位项目经理开玩笑说,"方法论源自项目经理的恐惧,这没错。但最糟糕的是整个团队只有项目经理一我的恐惧,若是可以作到人人的恐惧,那你们也就没有什么好恐惧的了。"
 
这句话提醒了咱们,若是一个团队的精神就是力求成功,那么这支团队的心态就和其它的团队不一样了,尤为是对待错误的心态上。根本就没有必要花费大量的精力来预防错误,错误犯了就犯了,即时改正就能够了。这其实就是渴望成功的心态。
 
方法论的艺术管理,被称为科学和艺术的融合体,而管理的艺术性部分很大程度的体现为人的管理上。我说,方法学,同样是科学和艺术的融合体。这是有依据的,其实方法论和管理学是近亲关系,管理学中有一门分支是项目管理,而在软件组织中,项目管理是很是重要的,方法学就是一种针对软件开发的一种特定的项目管理(或是项目管理的一个子集)。
 
重型方法最大的一个问题就在于他不清楚或忽略了艺术这个层次,忽视了人的因素,把人作为一个计量单位,一种资源,一种线性元素。而人的要素在软件开发中是很是重要的,软件开发其实是一种知识、智力的转移过程,最终造成的产品是一种知识产品,它的成本取决于开发者的知识价值,所以,人是最重要的因素。而人这个要素是很难衡量的,每一个人都有不一样的个性、想法、经验、经历,这么多复杂的因素加在一块儿,就致使了人的不可预见性。所以,咱们强调管人的艺术。
 
最简单的例子是,在重型方法中,咱们的基本假设是对人的不信任。项目经理要控制项目。但不信任就会产生不少的问题,好比士气不高,计划赶不上变化,创新能力低下,跳槽率升高等等。人都是但愿被尊重的,技术人员更看重这一点,而不少公司也口口声声说本身多么多么以人为本,但是采用的倒是以不信任人为前提的开发方法,言行不一。
 
咱们说敏捷方法的出发点是相互信任,作到这一点是很难的,可是一旦作到了,那这个团队就是很是具备竞争力的。所以,这就产生了一个问题,在没有作到彻底的相互信任以前,咱们到底相不相信他人呢,这就是我提到的艺术性的问题,何时你要相信人?何时你不相信人,这些都是须要权衡的问题,也都是表现你艺术性的问题。
 
敏捷敏捷表明着有效和灵活。咱们称那些轻型的、有效的方法为敏捷方法。在重型方法中,咱们在一些没必要要、重复的中间环节上浪费了太多的精力,而敏捷则避免了这种浪费。
 
咱们的文章将会重点的讨论敏捷(Agile)方法论的思想,敏捷这个名字的前身就是轻型。
 
目前已经有了一个敏捷联盟,他们制定了敏捷宣言:
 
Individuals and interactions over processes and tools. Working software over comprehensive documentation. Customer collaboration over contract negotiation. Responding to change over following a plan.
 
而我对敏捷的理解包括了几个方面:
· 较低的管理成本和高质量的产出。软件开发存在两个极端:一个是没有任何的管理成本,全部的工做都是为了软件的产出,可是这种方式却每每致使软件开发过程的混沌,产品的低质量,团队士气的低落。另外一个是大量管理活动的加入,评审、变动管理,缺陷跟踪,虽然管理活动的加入可以在必定程度上提升开发过程的有序性,可是成本却所以提升,更糟糕的是,很容易致使团队的低效率,下降创新能力。所以,敏捷方法视图寻找一个平衡点,用低成本的管理活动带来最大的产出,即软件的高质量。
 
· 尊重人性。敏捷方法尊重人性,强调效率。软件开发能够说是一种脑力的投入,若是不能保证开发人员的自愿投入,产品就确定要打折扣。事实屡次的证实,一个愿意投入的开发人员和一个不肯意投入的开发人员效率相差在三倍以上,对组织的贡献更是在十倍以上。
 
· 沟通和反馈是一切的基础。咱们已经讨论过沟通的重要程度,而即时的反馈是拥抱变化的前提条件。
 
· 客户是上帝。没有客户就没有一切,客户的重要性能够用一句话来形容,就是以合理的成本建造合适的软件(build the right system at the right cost)。
 
敏捷其实也有轻重之分,关键在因而否可以作到有效和灵活。所以,敏捷方法论提倡的一个思想是"恰好够(barely sufficient)"。不过这个"恰好够"可不是那么容易判断的。一支8我的的团队采用XP方法,随着方法的熟练使用,团队的能力在不断的加强,可以处理的问题越愈来愈复杂,也许他们可以处理采用重型方法的20我的团队可以处理的问题。但是若是团队的人数忽然增长到12人,这支团队确定就会出问题,他的表现可能还不如那支20我的的团队了。人数增长了的时候,原先的方法确定还作适当的调整,好比说,在原先的敏捷方法上增长一些重型方法的技巧。咱们不可以要求一支6我的的团队和一支20我的的团队用一样的方法,前者可能采用轻一些的敏捷方法,后者可能采用重一些的敏捷方法,关键的问题在于,两支团队都把重点放在沟通、反馈、频繁交付软件这些关键的因素上,也就是作到有效和灵活。
 
架构设计架构(Architecture)(也有被称为体系结构的)是软件设计中很是重要的一个环节。软件开发的过程当中只要需求和架构肯定以后,这个软件就基本上能够定型了。这就比如骨骼肯定了,这我的的体形就不会有很大的变化。所以我选择了架构设计来讨论敏捷软件开发(需求我已经写过了)。
 
咱们在前面讨论过超集和子集的概念,所以咱们接下去要讨论的架构设计也是一个很小的子集。方法论若是没有经历过多个项目的检验是不能称为成功的方法论的,我也并不认为个人架构设计就是一个好的方法论,但引玉还需抛砖,他的主要目的是为了传播一种思想。所以,我采用了模式语言(PLOP)作为写做架构设计的形式,主要的缘由就是模式是一种很好的组织思想的方法。所以,在咱们接下去的历程中,咱们集中讨论的东西就围绕着架构、方法学、敏捷这三个要素展开。
 
这篇文章并非讨论如何编码实现软件架构的,也不要单纯的把它看做架构设计的指南,其实文中的不少思想来自于方法论,所以提到的不少架构设计的思想也适用于其它工做,若是可以了解这一点,看这篇文章的收获可能会更多一些。
 
经过上一章的介绍,咱们对敏捷和方法有了一个大体的了解,从这一章起,咱们开始对软件开发过程当中架构设计的研究。
 
记住一点,咱们并非为了架构设计而研究架构设计,咱们的目的在于敏捷方法学的应用。架构设计是一种权衡(trade-off)。一个问题老是有多种的解决方案。而咱们要肯定惟一的架构设计的解决方案,就意味着咱们要在不一样的矛盾体之间作出一个权衡。
咱们在设计的过程老是能够看到不少的矛盾体:开放和整合,一致性和特殊化,稳定性和延展性等等。
 
任何一对矛盾体都源于咱们对软件的不一样指望。但是,要知足咱们但愿软件稳定运行的要求,就必然会影响咱们对软件易于扩展的指望。咱们但愿软件简单明了,却增长了咱们设计的复杂度。没有一个软件可以知足全部的要求,由于这些要求之间带有天生的互斥性。而咱们评价架构设计的好坏的依据,就只能是根据不一样要求的轻重缓急,在其间作出权衡的合理性。
 
目标咱们但愿一个好的架构可以:
· 重用:为了不重复劳动,为了下降成本,咱们但愿可以重用以前的代码、以前的设计。重用是咱们不断追求的目标之一,但事实上,作到这一点可没有那么容易。在现实中,人们已经在架构重用上作了不少的工做,工做的成果称为框架(Framework),好比说Windows的窗口机制、J2EE平台等。可是在企业商业建模方面,有效的框架还很是的少。
· 透明:有些时候,咱们为了提升效率,把实现的细节隐藏起来,仅把客户需求的接口呈现给客户。这样,具体的实现对客户来讲就是透明的。一个具体的例子是咱们使用JSP的tag技术来代替JSP的嵌入代码,由于咱们的HTML界面人员更熟悉tag的方式。
· 延展:咱们对延展的渴求源于需求的易变。所以咱们须要架构具备必定的延展性,以适应将来可能的变化。但是,如上所说,延展性和稳定性,延展性和简单性都是矛盾的。所以咱们须要权衡咱们的投入/产出比。以设计出具备适当和延展性的架构。 · 简明:一个复杂的架构不管是测试仍是维护都是困难的。咱们但愿架构可以在知足目的的状况下尽量的简单明了。可是简单明了的含义到底是什么好像并无一个明确的定义。使用模式可以使设计变得简单,但这是创建在我熟悉设计模式的基础上。对于一个并不懂设计模式的人,他会认为这个架构很复杂。对于这种状况,我只能对他说,去看看设计模式。
· 高效:不管是什么系统,咱们都但愿架构是高效的。这一点对于一些特定的系统来讲尤为重要。例如实时系统、高访问量的网站。这些值的是技术上的高效,有时候咱们指的高效是效益上的高效。例如,一个只有几十到一百访问量的信息系统,是否是有必要使用EJB技术,这就须要咱们综合的评估效益了。
· 安全:安全并非咱们文章讨论的重点,倒是架构的一个很重要的方面。
规则为了达到上述的目的,咱们一般须要对架构设计制定一些简单的规则:功能分解顾名思义,就是把功能分解开来。
 
为何呢?咱们之因此很难达到重用目标就是由于咱们编写的程序常常处于一种好像是重复的功能,但又有轻微差异的状态中。
 
咱们不少时候就会经不住诱惑,用拷贝粘贴再作少许修改的方式完成一个功能。这种行为在XP中是坚定不被容许的。XP提倡"Once and only once",目的就是为了杜绝这种拷贝修改的现象。为了作到这一点,咱们一般要把功能分解到细粒度。不少的设计思想都提倡小类,为的就是这个目的。因此,咱们的程序中的类和方法的数目就会大大增加,而每一个类和方法的平均代码却会大大的降低。但是,咱们怎么知道这个度应该要如何把握呢,关于这个疑问,并无明确的答案,要看我的的功力和具体的要求,可是通常来讲,咱们能够用一个简单的动词短语来命名类或方法的,那就会是比较好的分类方法。
 
咱们使用功能分解的规则,有助于提升重用性,由于咱们每一个类和方法的精度都提升了。这是符合大天然的原则的,咱们研究天然的主要的一个方向就是将物质分解。
 
咱们的思路一样能够应用在软件开发上。除了重用性,功能分解还能实现透明的目标,由于咱们使用了功能分解的规则以后,每一个类都有本身的单独功能,这样,咱们对一个类的研究就能够集中在这个类自己,而不用牵涉到过多的类。根据实际状况决定不一样类间的耦合度虽然咱们老是但愿类间的耦合度比较低,可是咱们必须客观的评价耦合度。
 
系统之间不可能老是松耦合的,那样确定什么也作不了。而咱们决定耦合的程度的依据何在呢?简单的说,就是根据需求的稳定性,来决定耦合的程度。
 
对于稳定性高的需求,不容易发生变化的需求,咱们彻底能够把各种设计成紧耦合的(咱们虽然讨论类之间的耦合度,但其实功能块、模块、包之间的耦合度也是同样的),由于这样能够提升效率,并且咱们还能够使用一些更好的技术来提升效率或简化代码,例如Java中的内部类技术。但是,若是需求极有可能变化,咱们就须要充分的考虑类之间的耦合问题,咱们能够想出各类各样的办法来下降耦合程度,可是概括起来,不外乎增长抽象的层次来隔离不一样的类,这个抽象层次能够是具体的类,也能够是接口,或是一组的类(例如Beans)。
 
咱们能够借用Java中的一句话来归纳下降耦合度的思想:"针对接口编程,而不是针对实现编程。" 设计不一样的耦合度有利于实现透明和延展。对于类的客户(调用者)来讲,他不须要知道过多的细节(实现),他只关心他感兴趣的(接口)。这样,目标类对客户来讲就是一个黑盒子。若是接口是稳定的,那么,实现再怎么扩展,对客户来讲也不会有很大的影响。
 
之前那种牵一发而动全身的问题彻底能够缓解甚至避免。其实,咱们仔细的观察GOF的23种设计模式,没有一种模式的思路不是从增长抽象层次入手来解决问题的。一样,咱们去观察Java源码的时候,咱们也能够发现,Java源码中存在着大量的抽象层次,初看之下,它们什么都不干,可是它们对系统的设计起着重大的做用。
 
够用就好咱们在上一章中就谈过敏捷方法很看重恰好够用的问题,如今咱们结合架构设计来看:
在一样都可以知足须要的状况下,一项复杂的设计和一项简单的设计,哪个更好。从敏捷的观点来看,必定是后者。由于目前的需求只有10项,而你的设计可以知足100项的需求,只能说这是种浪费。你在设计时彻底没有考虑成本问题,不考虑成本问题,你就是对开发组织的不负责,对客户的不负责。
应用模式这篇文章的写做思路不少来源于对模式的研究。所以,文章中处处均可以看到模式思想的影子。模式是一种整理、传播思想的很是优秀的途径,咱们能够经过模式的方式学习他人的经验。一个好的模式表明了某个问题研究的成果,所以咱们把模式应用在架构设计上,可以大大加强架构的稳定性。
 
抽象架构的本质在于其抽象性。它包括两个方面的抽象:业务抽象和技术抽象。架构是现实世界的一个模型,因此咱们首先须要对现实世界有一个很深的了解,而后咱们还要可以熟练的应用技术来实现现实世界到模型的映射。所以,咱们在对业务或技术理解不够深刻的状况下,就很难设计出好的架构。
 
固然,这时候咱们发现一个问题:怎样才能算是理解足够深刻呢。我认为这没有一个绝对的准则。
 
一次,一位朋友问我:他如今作的系统有很大的变化,原先设计的工做流架构不能知足如今的要求。他很但愿可以设计出足够好的工做流架构,以适应不一样的变化。可是他发现这样作无异于从新开发一个lotus notes。
 
我听了他的疑问以后以为有两点问题:
首先,他的开发团队中并无工做流领域的专家。他的客户虽然了解本身的工做流程,可是缺少足够的理论知识把工做流提到抽象的地步。显然,他自己虽然有技术方面的才能,但就工做流业务自己,他也没有足够的经验。因此,设计出象notes那样的系统的前提条件并不存在。
 
其次,开发一个工做流系统的目的是什么。原先的工做流系统运做的很差,其缘由是有变化发生。所以才有改进工做流系统的动机出现。
但是,毕竟notes是为了知足世界上全部的工做流系统而开发的,他目前的应用确定达不到这个层次。所以,虽然作不到最优的业务抽象,可是咱们彻底能够在特定目的下,特定范围内作到最优的业务抽象。好比说,咱们工做流可能的变化是工组流路径的变化。
 
咱们就彻底能够把工做流的路径作一个抽象,设计一个能够动态改变路径的工做流架构。有些时候,咱们虽然在技术上和业务上都有所欠缺,没有办法设计出好的架构。可是咱们彻底能够借鉴他人的经验,看看相似的问题别人是如何解决的。这就是咱们前面提到的模式。
 
咱们不要把模式当作是一个硬性的解决方法,它只是一种解决问题的思路。Martin Fowler曾说:"模式和业务组件的区别就在于模式会引起你的思考。"
 
在《分析模式》一书中,Martin Fowler提到了分析和设计的区别。分析并不只仅只是用用例列出全部的需求,分析还应该深刻到表面需求的的背后,以获得关于问题本质的Mental Model。而后,他引出了概念模型的概念。概念模型就相似于咱们在讨论的抽象。
 
Martin Fowler提到了一个有趣的例子,若是要开发一套软件来模拟桌球游戏,那么,用用例来描述各类的需求,可能会致使大量的运动轨迹的出现。若是你没有了解表面现象以后隐藏的运动定律的本质,你可能永远没法开发出这样一个系统。
 
关于架构和抽象的问题,在后面的文章中有一个测量模式的案例能够很形象的说明这个问题。架构的一些误解咱们花了一些篇幅来介绍架构的一些知识。如今回到咱们的另外一个主题上来。
 
对于一个敏捷开发过程,架构意味着什么,咱们该如何面对架构。这里咱们首先要澄清一些误解:
 
 · 误解1:架构设计须要很强的技术能力。从某种程度来讲,这句话并无很大的错误。毕竟,你的能力越强,设计出优秀架构的概率也会上升。可是能力和架构设计之间并无一个很强的联系。即便是普通的编程人员,他同样有能力设计出能实现目标的架构。
 
 · 误解2:架构由专门的设计师来设计,设计出的蓝图交由程序员来实现。咱们之因此会认为架构是设计师的工做,是由于咱们喜欢把软件开发和建筑工程作类比。可是,这二者其实是有着很大的区别的。关键之处在于,建筑设计已经有很长的历史,已经发展出完善的理论,能够经过某些理论(如力学原理)来验证设计蓝图。但是,对软件开发而言,验证架构设计的正确性,只可以经过写代码来验证。所以,不少看似完美的架构,每每在实现时会出现问题。
 
 · 误解3:在一开始就要设计出完善的架构。这种方式是最传统的前期设计方式。这也是为XP所摒弃的一种设计方式。主要的缘由是,在一开始设计出完美的架构根本就是在自欺欺人。由于这样作的基本假设就是需求的不变性。但需求是没有不变的(关于需求的细节讨论,请参看拙做『需求的实践』)。这样作的坏处是,咱们一开始就限制了整个的软件的形状。而到实现时,咱们虽然发现原来的设计有失误之处,但却不肯意面对现实。这使得软件畸形的生长。本来一些简单的问题,却由于别扭的架构,变得很是的复杂。这种例子咱们常常能够看到,例如为兼容前个版本而致使的软件复杂性。而2000年问题,TCP/IP网络的安全性问题也从一个侧面反映了这个问题的严重性。
 
· 误解4:架构蓝图交给程序员以后,架构设计师的任务就完成了。
 
和误解2同样,咱们借鉴了建筑工程的经验。咱们看到建筑设计师把设计好的蓝图交给施工人员,施工人员就会按照图纸建造出和图纸如出一辙的大厦。因而,咱们也企图在软件开发中使用这种模式。这是很是要命的。软件开发中缺少一种通用的语言,可以充分的消除设计师和程序员的沟通隔阂。有人说,UML不能够吗?UML的设计理念是好的,能够减轻沟通障碍问题。但是要想彻底解决这个问题,UML还作不到。
首先,程序员都具备个性化的思惟,他会以本身的思惟方式去理解设计,由于从设计到实现并非一项机械的劳动,仍是属于一项知识性的劳动(这和施工人员的工做是不一样的)。
 
此外,对于程序员来讲,他还极有可能按照本身的想法对设计图进行必定的修改,这是很是正常的一项举动。更糟的是,程序员每每都比较自负,他们会潜意识的排斥那些未通过本身认同的设计。架构设计的过程模式一般咱们认为模式都是用在软件开发、架构设计上的。其实,这只是模式的一个方面。模式的定义告诉咱们,模式描述了一个特定环境的解决方法,这个特定环境每每重复出现,制定出一个较好的解决方法有利于咱们在将来能有效的解决相似的问题。
 
其实,在管理学上,也存在这种相似的这种思惟。称为结构性问题的程序化解决方法。因此呢,咱们彻底能够把模式的思想用在其它的方面,而目前最佳的运用就是过程模式和组织模式。在咱们的文章中,咱们仅限于讨论过程模式。咱们讨论的过程仅限于面向对象的软件开发过程。咱们称之为OOSP(object-oriented software process )。由于咱们的过程须要面向对象特性的支持。固然,咱们的不少作法同样能够用在非OO的开发过程当中,可是为了达到最佳的效果,我建议您使用OO技术。那么,咱们应该如何避开这些误区呢,或者,换句话说,敏捷软件开发是如何作架构设计的。
这里有几种过程模式:图 2. 敏捷架构过程模式概览(High-Level)在接下去的篇幅中,咱们会逐一对各类过程模式进行介绍。而后再站在全局的角度分析各个模式之间的关系,并将之概括为架构设计的模式。敏捷型架构设计咱们说咱们这里列出的过程模式是敏捷型的,关于这一点咱们会在接下去的各个章节中验证这一点。咱们列出的各个过程模式并非彻底照搬敏捷型方法,由于在各类敏捷型方法中,某些技巧适合架构设计,某些方法则不适合架构设计。所以,咱们在采用一种方法和技术前,咱们会问本身几个简单的问题:该方法/技巧有什么价值? 该方法/技巧须要多大的投入?从建立、维护、培训等多方面估计。 比较该方法/技巧的投入和价值,它还值得咱们采用吗? 是否还有其它价值/投入比更高的方法/技巧呢?
 
在咱们的文章中,每一种方法/技巧的讨论都回答了前三个问题,至于第四个问题,但愿有同行可以告诉我。 咱们说,和重型方法偏重于计划、过程和中间产物不一样,敏捷方法更加看重人和沟通。人和沟通永远是第一位的,而计划、过程和中间产物,那只是保证沟通、实现目标的手段。这并非说计划、过程、中间产物不重要,只是不可以本末倒置注:咱们把中间产物定义为为了实现跨边界的沟通而制定的文档、模型、代码。例如设计文档、数据模型等。参考RUP的Artifact。
 
评判软件成功的标准有不少,对于敏捷方法论来讲,成功的标准首先在于交付可用的软件。为了保证软件的可用性,最重要的就是作好需求。作好需求的方法有不少(参见拙做需求的实践),但这并非咱们讨论的主题。对于咱们要开始的架构设计的工做来讲,从需求出发来设计架构,这就是保证软件可用性的一个基本的保证。
 
 Context
 
咱们如何开始咱们的架构设计工做? Problem 咱们在进行架构设计的时候,每每主要考虑的都是平台、语言、开发环境、数据库等一些基本问题,但是对于和客户的具体状况密切相关的一些问题却不多系统的考虑。甚至还存在一种误区,认为架构设计无非就是写一些空话,套话。这样子作出来架构设计,如何用于指导软件的实现呢? IT界的技术层出不穷,面对着如此之多的技术、平台、框架、函数库,咱们如何选择一组适合软件的技术?每个客户的软件都有自身的特色,如何才可以设计出符合客户利益的架构?软件中每每都充斥着众多的问题,在一开始就把全部的问题都想清楚每每很难作到,可是若是不解决问题,风险又居高不下。
 
Solution
 
针对需求设计架构。架构设计就是铺设软件的主管道(例1)。咱们根据什么来制定主管道的粗细、路径等因素呢?很明显,是根据城市的人口、地理位置、水源等因素来决定的。对应到软件设计也是同样的。城市的各因素就是软件中的各类需求:功能需求、非功能需求、变化案例等等。通常来讲,功能需求决定业务架构、非功能需求决定技术架构,变化案例决定架构的范围。需求方面的知识告诉咱们,功能需求定义了软件可以作些什么。咱们须要根据业务上的需求来设计业务架构,以使得将来的软件可以知足客户的须要。非功能需求定义了一些性能、效率上的一些约束、规则。而咱们的技术架构要可以知足这些约束和规则。变化案例是对将来可能发生的变化的一个估计,结合功能需求和非功能需求,咱们就能够肯定一个需求的范围,进而肯定一个架构的范围。
 
从例2中,咱们看到自已字处理软件的几种需求的范例。真正的字处理软件要复杂的多。而咱们最主要的就是必须认识到,架构是来自于需求的。有什么样的需求就有什么样的架构。试想一下,若是咱们没有对速度的要求,咱们还须要考虑这方面的设计吗?咱们上面提到了几种类型的需求对架构的影响,其实还有一个很重要的需求,就是环境的需求。这并非一个很重要的需求,可是对于部署(deployment)架构设计来讲就特别重要。毕竟,咱们开发出的软件是要上"战场"的,充分的考虑部署问题是很是有必要的。
 
例1:城市中自来水管的架设是一项很是的复杂的工程。为了须要知足每家每户的须要,自来水管组成了一个庞大的网络。在这样一个复杂的网络中,如何完成铺设的任务呢。通常的作法是,先找出问题的根源,也就是水的源头。从水源铺设一条管道通至城市,而后根据城市的区域划分,设计出主管道,剩下的就是使用的问题了,每家每户的管道最终都是连到主管道上的。所以,虽然自来水网络庞大复杂。可是真正的主管道的很是简单的。
 
例2:咱们打算开发一个字处理软件,功能需求能够简单归纳为格式化用户输入的文字,非功能需求多是格式化大小为1000K的一段文字的处理速度不能低于10S,变化案例多是推出多种语言版本。那么咱们在设计业务架构的时候,咱们会集中于如何表示文字、图象、媒体等要素,咱们该须要有另外的技术架构来处理速度问题,好比缓冲技术,对于变化案例,咱们也要考虑相应的架构,好比把字体独立于程序包的设计。
 
从需求到架构。在需求阶段,咱们能够获得一些表明需求调研成果的中间产物。好比说,CRC卡片、基本用例模型、用户素材、界面原型、界面原型流程图、非功能需求、变化案例等。咱们在架构设计阶段的主要工做就是要把这些需求阶段的中间产物转换为架构设计阶段的中间产物。
图 3.  需求阶段的中间产物 其实,架构设计就是要完成两项工做,一是分析,二是设计。分析是分析需求,设计则是设计软件的大体结构。不少的方法论把分析和设计两种活动分开来,但其实这二者是很难区分的,作分析的时候会想到如何设计,而思考如何设计反过来又会影响分析的效果。能够说,他们二者之间是相互联系和不断迭代的。这种形态咱们将会在后面的迭代设计模式中详细的讨论。在敏捷方法论中,需求最好是迭代进行的,也就是说一点一点的做需求。这种作法在那些需求变化快的项目中尤为适用。因为咱们采用的流程是一种迭代式的流程,这里咱们将会面临着如何对待上一次迭代的中间产物的问题。若是咱们每一次迭代都须要修改已存在的中间产物,那么这种维护的成本未免过大。所以,敏捷方法论的基本作法是,扔掉那些已经没有用处的中间产物。还记得在第一章的时候,咱们强调说软件要比文档重要。咱们生成中间产物的目的都是为了生成最终的程序,对于这些已经完成做用的模型,没有必要付出额外的维护成本。不要断章取义的采用抛弃模型的作法。由于,抛弃模型的作法须要一个适合环境的支持。后面会针对这个话题开展大范围的讨论。这里咱们简单的作一个了解:
 
 · 简单化:简单的模型和简单的程序。模型和程序越复杂,就须要更多的精力来处理它们。所以,咱们尽量的简化它们,为的是更容易的处理它们。
 
· 高效的沟通渠道:经过加强沟通的效果来减小对中间产物的须要。试想一下,若是我随时可以从客户那里获得需求的细节资料,那前期的需求调研就没有必要作的太细致。
 
· 角色的交叉轮换:开发人员之间创建起交换角色的机制,这样,可以尽可能的避免各子系统诸侯割据的局面。
 
· 清晰的流程:或者咱们能够称之为明确的过程。过程在方法论中向来都是一个重点,敏捷方法论也不例外。开发人员可以清楚的知道,今天作什么,明天作什么。过程不是给别人看的,而是给本身用的。
 
· 工具:好用的工具可以节省大量的时间,这里的工具并不只仅指CASE工具,还包括了版本控制工具、自动化测试工具、画图工具、文档制做和管理工具。使用工具要注意成本和效益的问题。
 
· 标准和风格:语言不通是沟通的一个很大的障碍。语言从某个角度来看属于一种标准、一种风格。所以,一个团队若是采用一样的编码标准、文档标准、注释风格、制图风格,那么这个团队的沟通效率必定很是的高。
 
若是上述的环境你都不具有,或是欠缺好几项,那你的文档的模型仍是留着的好。仅针对需求设计架构仅针对需求设计架构的含义就是说不要作将来才有用的事情。有时候,咱们会把架构考虑的很是复杂,主要的缘由就是咱们把不少将来的因素放入到如今来考虑。或者,咱们在开发第一个产品的时候就视图把它作成一个完美的框架。
 
以上的这两种思路有没有错呢?没有错,这只是如何看待投入的问题,有人但愿开始的时候多投入一些,这样后续的投入就会节省下来。但在现实中,因为需求的不肯定性,但愿经过增长开始阶段的投入来将下降将来的投入每每是难以作到的,框架的设计也绝对不是可以一蹴而就的,此这种作法并非一个好的作法。因此咱们在后头会着重论述架构设计的简单性和迭代过程,也就是由于这个理由。模式模式将能够帮助咱们抓住重点。
 
设计模式在书的一开始(第二章)就讨论了一个设计一个文档编辑器的问题。为了解决设计文档编辑器引出的七个问题,一共使用了8种不一样的模式。这8种模式的组合其实就是架构,由于它们解决的,都是系统中最高层的问题。在实践中,人们发现架构也是存在模式的。好比,对于系统结构设计,咱们使用层模式;对于分布式系统,咱们使用代理模式;对于交互系统,咱们使用MVC(模型-视图-控制器)模式。模式原本就是针对特定问题的解,所以,针对需求的特色,咱们也能够采用相应的模式来设计架构。在sun网站上提供的宠物商店的范例中,就把MVC模式的思想扩展成为架构的思想,用于提供不一样的界面视图: MVC架构图,这里提供原图的概览,查看其出处请点击这里。
 
咱们能够了解到在图的背后隐藏着的需求:系统须要支持多种用户界面,包括为普通用户提供的HTML界面,为无线用户提供的WML界面,为管理员提供的Swing界面,以及为B2B业务设计的WebService界面。这是系统最重要的需求,所以,系统的设计者就须要肯定一个稳定的架构,以解决多界面的问题。相对于多界面的问题,后端的业务处理逻辑都是一致的。好比HTML界面和WML界面的功能并无太大的差异。把处理逻辑和界面分离开来还有额外的好处,能够在添加功能的同时,不涉及界面的改动,反之亦然。这就是咱们在第二篇中提到的耦合度的问题。 MVC模式正能够适用于解决该问题。系统使用控制器来为业务逻辑选择不一样的界面,这就完成了MVC架构的设计思路。
 
在架构设计的工做中,咱们手头上有模式这样一张好牌,有什么理由不去使用它呢?抓住重点在架构设计一开始,咱们就说架构是一种抽象,那就是说,架构设计摒弃了具体的细节,仅仅抓住软件最高层的概念,也就是最上层、优先级最高、风险最大的那部分需求。
 
咱们考虑、分析、解决一个问题,必定有一个渐进的过程。架构设计就是解决问题其中比较早期的一个阶段,咱们不会在架构设计这个阶段投入过多的时间(具体的缘由在下文会有讨论),所以关键点在于咱们要可以在架构设计中把握住需求的重点。好比,咱们在模式一节中提到了分布式系统和交互系统,分布和交互就是这两个系统的重点。那么,若是说咱们面对的是一个分布式的交互系统,那么,咱们就须要把这两种特性作为重点来考虑,并以此为基础,设计架构。
 
而咱们提到的宠物商店的范例也是相似的,除了MVC的架构,还有不少的设计问题须要解决,例如用于数据库访问的数据对象,用于视图管理的前端控制器,等等(具体使用到的架构模式能够访问sun的网站)。可是这些相对于MVC模式来讲,属于局部的,优先级较低的部分,能够在架构肯定后再来设计。架构设计和领域专家一个架构要设计的好,和对需求的理解是分不开的。所以在现实中,咱们发现业务领域专家凭借着他对业务领域的了解,可以帮助开发人员设计出优秀的架构来。架构是须要抽象的,它是现实社会活动的一个基本模型,而业务领域的模型仅仅凭开发人员是很难设计出来的。
 
在ERP的发展史上,咱们看到MRP发展为MRPII,在发展到闭环MRP,直到发展成为如今的ERP,主要的因素是管理思想的演化,也就是说,对业务领域的理解进步了,架构才有可能进步。所以,敏捷型架构设计的过程当中,咱们也很是强调领域专家的做用。
 
例3:信贷系统在一个银行的信贷账务处理系统中,咱们应该如何把握最初的架构思路呢?从需求上来看,这个信贷账务处理系统有几个特色:它不是一个单独的系统,它须要和外部的其它系统交互,例如信贷业务系统、网上银行、数据仓库系统等。在全部的需求中,最复杂的就是它的利息计算的需求,它要求可以支持多种的利息算法。所以,咱们的架构设计首先是从系统的全局环境开始考虑。其它系统和该系统的关系如何,应该如何设计系统间的接口。经过需求的调研,系统的接口分为4类:和企业外部系统的接口。信贷系统须要链接到人民银行的系统。和企业内部系统的接口。信贷系统须要可以为数据仓库系统和网上银行系统提供数据。和平级系统的接口。信贷系统须要从平级的账户、资金、财务系统中取数据,并向账户、押汇系统发送数据。
具体的实现策略并不在咱们的讨论范围以内,可是能够看到,架构策略的制定是以需求为基础的。咱们能够把这部分的需求概括为技术架构或平台架构。而后是利息算法的问题,咱们通过统计,目前的利息计算方式有四种,可预见到的还有两种。在一开始的阶段,咱们并不须要考虑具体算法的实现,可是咱们须要考虑算法的实现框架,所以咱们很天然的想到Strategy模式能够胜任这一工做,把不一样的利息算法封装起来。而咱们的工做重点也就转到定义利息算法的接口问题。经过分析、比较多种利息算法,咱们定义了一个最初始的算法接口,而后由不一样的利息算法来实现算法接口。虽然,这个接口目前还不是很完整,可是它会在接下去的开发过程当中慢慢的完善起来。这部分的需求属于业务架构的一部分。考虑到系统的结构很是的复杂,所以在系统结构的处理上,咱们采用了层模式作为系统的基本结构。此外,在每一个层,咱们还定义了几个子模块来处理特定的问题。这样,咱们就能够将复杂的功能有序的组织起来。通过上述的分析,咱们对系统的架构有了一个简单的认识,可是尚未结束,一个架构应该包括系统的各个基本部分,所以,咱们还要考虑票据处理、报表、账务处理等环节,可是一开始就考虑周详,这要花费大量的时间,所以咱们只是简单的定义了一个原始的架构,而后在后续的开发过程当中把这个架构完善起来。
 
团队设计是敏捷方法论中很重要的一项实践。咱们这里说的团队,指的并非复数的人。一群人就是一群人,并无办法构成团队。要想成为团队,有不少的工做要作。 咱们之因此考虑以团队为单位来考虑架构设计,是由于软件开发自己就不是一件我的的事情,架构设计更是如此。单我的的思惟难免有考虑欠妥之处,单我的的学识也不可能覆盖全部的学科。而组织有效的团队却可以弥补这些缺憾。
Context
 
谁来负责架构的设计?
 
Problem
 
在咱们的印象中,总认为架构设计是那些所谓架构设计师的专属工做,他们每每拥有丰富的设计经验和相关的技能,他们不用编写代码,就可以设计出理论上尽善尽美的架构,配有精美的图例。
 
问题1:理论上设计近乎完美的架构缺少程序的证实,在实际应用中每每会出这样那样的问题。
 
问题2:设计师设计架构带有很大的主观性,每每会忽视客户的需求,致使架构没法知足需求。
 
问题3:实现的程序员对这种架构有抵触的情绪,或是由于不理解架构而致使架构实现的失败。
 
问题4:架构师设计架构主要是依据本身的大量经验,设计出的架构不能真实的反映目前的软件须要。
 
Solution
 
团队设计的理论依据是群体决策。和我的决策相比,群体决策的最大好处就是其结论要更加的完整。而群体决策虽然有其优势,但其缺点也是很明显的:须要额外付出沟通成本、决策效率低、责任不明确、等等。可是群体决策若是可以组织得当的话,是可以在架构设计中发挥很大的优点的。避免象牙塔式的架构设计对软件来讲,架构设计是一项相当重要的工做。这样的工做交给某我的是很是危险的。即使这我的再怎么聪明,他也可能会遗漏部分的细节。组织有效的团队的力量是大大超过我的的力量的,所以团队的成果较之我的的成果,在稳定性和思考的周密程度上,都要更胜一筹。 Scott W. Ambler在其著做中给出了象牙塔式架构(ivory tower architecture)的概念: An ivory tower architecture is one that is often developed by an architect or architectural team in relative isolation to the day-to-day development activities of your project team(s). 中国如今的软件开发行业中也逐渐出现了象牙塔式的架构设计师。这些架构师并不参与实际的程序编写,他的工做就是为项目制做出精美的架构模型,这种架构模型在理论上是至关完美的。
 
例1:在XP中,咱们基本上看不到架构设计的影子。并非说采用XP技术的团队就不须要架构设计。XP不存在专门的设计时期,它提倡使用一些简单的图例、比喻的方式来表达软件的架构,而这种的架构设计是无时无刻不在进行的。其实,XP中的设计采用的就是团队设计的方式,结队编程(Pair Programming)和代码的集体全部制(Collective Ownership)是团队设计的基础,也就是基于口述的沟通方式。经过采用这样的方式,XP几乎不须要文档来表达架构的设计。 优秀的架构师可以充分的利用现有框架,减小软件的投入,加强软件的稳定性。这些都没有错,可是问题在于“过犹不及”。象牙塔式架构师每每会出现文章开始指出的那些问题。架构设计其实并非很是复杂的工做,但它要求开发人员具有相关的技能、经验以及对问题域有必定的了解。开发人员每每都具备相关的技术技能(编程、数据库设计、建模),而对问题域的理解能够从用户和行业专家那里得到帮助。所以,在理论上,咱们要实现架构设计的团队化是彻底可能的。在上面的象牙塔式架构定义中,咱们看到架构师和平常的开发工做是隔绝的。这样的设计出的架构有很大的局限性。在现实中,咱们还会发现另一种角色,他来自于开发团队外部,为开发人员提供相关的技术或业务的培训。这种角色称为教练,在软件开发中是很是重要的角色,不可以和象牙塔式架构设计师之间画等号。选择你的设计团队。 软件的架构在软件的生命周期的全过程当中都很重要,也就是说,软件开发团队中的全部人员都须要和架构打交道。所以,最好的团队组织方式是全部开发人员都参与架构的设计,咱们称这种方式为全员参与。全员参与的方式保证了全部开发人员都可以对架构设计提出本身的看法,综合多方面的意见,在全体开发人员中达成一致。这种方式尤为适合于一些小的团队。仍是会有不少的团队因为种种的缘由不适合采用全员参与的方式。那么,组织优秀的开发人员组成设计组也是比较好的方式。通常,咱们选择那些在项目中比较重要的,有较多开发经验,或是理论扎实的那些人来组成设计组。固然,若是你考虑到为组织培养后续力量,你也可让一些新手加入设计组,或是你以为本身的开发力量不足,邀请外部的咨询力量介入,这彻底取决于具体的状况。 设计组不一样于咱们以前提到的象牙塔式架构设计师。设计组设计出来的架构只能称为原始架构,它是须要不断的反馈和改进的。所以,在架构实现中,设计组的成员将会分布到开发团队的各个领域,把架构的思想带给全部开发人员,编写代码来检验架构,并得到具体的反馈,而后全部的成员再集中到设计组中讨论架构的演进。团队设计中存在的问题在团队设计的过程,咱们会遇到各类各样的问题,首当其冲的就是沟通成本的问题。架构设计时,需求还没有被充分理解,软件的设计思路还处于萌发的状态。这样的状况下,团队的每位成员对软件都有独特的看法,这些可能有些是相同的,有些是互斥的。就比如盲人摸象同样,他们的观点都表明了软件的一部分或是一方面,可是没有办法表明软件的所有。在敏捷方法论中,咱们的每个流程都是迅速进行、不断改进的。架构设计也是同样,咱们不可能在一次架构设计上花费更多的时间。而团队决策老是倾向于较长的讨论和权衡。例2中的问题在架构设计中时有发生,纯技术的讨论很容易上升称为争吵。这种状况几乎没有办法彻底避免。团队型的决策必然会发生观念的冲突。控制必定程度内的观念的冲突对团队的决策是有益,可是若是超出了这个程度就意味着失控了,须要团队领导者的调节。而更重要的,咱们须要注意沟通的技巧:团队沟通团队进行架构设计的时候沟通是一个很是须要注意的问题,上述的情境在软件组织中是常常发生的,由于技术人员很天然认为本身的技术比别人的好,若是本身的技术受到质疑,那怕对方是抱着讨论的态度,也无异于自身的权威受到了挑战,面子是不管如何都须要捍卫的。而沟通若是带上了这样一层主观色彩,那么沟通讯息的受众就会潜意识的拒绝接受信息。相反,他会找出对方话语中的漏洞,准备进行反击。所以,咱们要注意培养一种良好的沟通氛围。在实际的观察中,我发现团队沟通中存在两种角色,一种是建议者,他们常常可以提出建议。一种是质疑者,他们对建议提出否认性的见解。这两种角色是可能互换的,如今的建议者可能就是刚才的质疑者。质疑者的发言是很能打击建议者的积极性的,而在一个脑力激荡的会议中,最好是你们都可以扮演建议者的角色,这就要求沟通会议的主持者可以掌握好这一点,对建议给予确定的评价,并鼓励你们提出新的建议。例2:敏捷方法很是注重的就是团队的沟通。沟通是一个颇有意思的话题,讲起来会花费大量的时间,咱们这里只是针对架构设计中可能存在的沟通问题作一个简单的讨论。咱们这里假设一个讨论情境,这个情境来源于真实的生活:项目主管徐辉、设计师李浩、设计师罗亦明正在讨论一个新的软件架构。 "李浩你认为这个软件数据库链接部分应该如何考虑?"徐辉问。李浩想了想,"我以为方案A不错…" "方案A确定有问题!这个软件和上一次的又不一样。"罗亦明打断了李浩的发言。 "你懂什么!你到公司才多久,方案A是通过很长时间的证实的!"发言被打断,李浩有点恼火,罗亦明进入公司没有多久,但在一些事情上总是和他唱反调。 "我进公司多久和方案A的错误有什么关系!" 在这样一种氛围中,会议的结果可想而知。 良好的沟通有助于架构设计工做的开展。一个成员的能力平平的团队,能够藉由良好的沟通,设计出优秀的架构,而一个拥有一个优秀成员的团队,若是缺少沟通,最后可能连设计都出不来。这种例子现实中能够找到不少。标准和风格 咱们老是在不知不觉之中使用各类各样的标准和风格。在团队设计中,咱们为了提升决策的效率,能够考虑使用统一的标准和风格。统一的标准和风格并非一朝一夕造成的。由于每一个人都有本身不一样的习惯和经历,强制性的要求开发人员使用统一的标准(风格)容易引发开发人员的不满。所以在操做上须要注意技巧。对架构设计而言,比较重要的标准(风格)包括如下的这些类别:界面设计 流程设计 建模规范 编码规范 持久层设计 测试数据 在个人经验中,有一些组织平时并不注意标准(风格)的积累,认为这种积累属于雕虫小技,但正是这些小技,可以很是有效的提升沟通的效率和下降开发人员的学习曲线。试想一下,若是一个团队中全部人写出的代码都是不一样标准和风格的,那么理解起来确定会困难许多。固然,咱们没有必要本身开发一套标准(风格)出来,现实中有不少能够直接借用的资料。最好的标准是UML语言,咱们能够从UML的官方网站下载到最新的规范,经常使用的编码标准更是随处可见。不过虽然有了统一的标准,若是风格不统一,一样会形成沟通的障碍。例以下图显示的类图,虽然它们表示的是同一个类,可是因为版型、可视性、详细程度的差异,看起来又很大的差异。而在其它的标准中,这种差异也是广泛存在的。所以,咱们在使用了统一的标准以后,还应该使用一样的风格。Scott W. Ambler专门成立了一个网站讨论UML的建模风格的相关问题,有兴趣的读者能够作额外的阅读。图 4. 两种风格的类图 在统一的风格的基础上更进一步的是使用术语。使用沟通双方都了解专门的术语,能够表明大量的信息。最好的术语的范例就是设计模式的模式名。若是沟通的双方都了解设计模式,那么一方只须要说这部分的设计能够使用工厂模式,另外一方就可以理解,而不用再详细的解释设计的思路。这种的沟通方式是最高效的,但它所须要的学习曲线也会比较陡。团队设计的四明确为了最大程度的提升团队设计的高效性,能够从4个方面来考虑: 一、明确目标 泛泛的召开架构讨论会议是没有什么意义的,一个没有鲜明主题的会议也不会有什么结果。在源自需求的模式中,咱们谈到说能够有非功能需求的架构,能够有功能需求的架构。所以,在进行团队设计以前,咱们首先也须要肯定,这次要解决什么问题,是讨论业务逻辑的架构,仍是技术架构;是全局性的架构,仍是各模块的架构。 二、明确分工 咱们之因此重视团队,很重要的额一个缘由就是不一样的成员有不一样的擅长的区域。有些成员可能擅长于业务逻辑的建模,有的擅长于原型设计,有的擅长于数据库设计,有的则擅长于Web编程。你可以想象一个软件没有界面吗?(有些软件多是这种状况)你可以想象一个软件只有数据库,而没有处理逻辑吗?所以,架构设计就须要综合的考虑各个方面,充分利用成员的优点。这就要求团队的各个成员都可以明确本身的分工。 三、明确责权除了明确本身的分工,每位成员都须要清楚本身的责任。没有责任,分工就不会有任何的效力。每位成员都须要明确本身要作些什么。固然,和责任相对的,没有成员还须要知道本身的权力是什么。这些清楚了,进行高效的沟通的前提就具有了。每次架构的讨论下来,每一个人都清楚,本身要作些什么,本身须要要求其余人作些什么,本身该对谁负责。若是这些问题回答不了,那此次的讨论就白费了。 四、明确沟通方式这里使用沟通方式可能有一点点不恰当,为了明确的表达意思,你们能够考虑信息流这个词。一个完整架构包括几个方面,分别都由那些人负责,如何产生,产生的整个过程应该是什么样的?这样的一个信息流程,囊括了上面提到的三个明确。若是团队的每个人都可以为架构的产生而努力,并顺利的设计出架构,那么这样的流程是完美的。若是你发现其中的一些人不知道作些什么,那么,这就是流程出问题的现象了。完美的流程还会有一个额外的副产品,架构产生以后,团队对于软件的设计已是很是的清晰了。由于咱们提倡的是尽量多的开发人员参与架构的设计。不只仅是架构讨论到这里,其实有不少的内容已经脱离了架构设计了。也就是说,不少的原则和技巧都是能够用于软件开发的其它活动的。至于哪一些活动可以利用这些方法呢?你们能够结合本身的实际状况,来思考这个问题。提示一点,关键的入手处在于目前效率较低之处。 XP很是强调简单的设计原则:可以用数组实现的功能决不用链表。在其它Agile方法中,简单的原则也被反复的强调。在这一章,咱们就对简单性作一个全面的了解。 Context 架构应该设计到什么程度? Problem 软件的架构都是很是的复杂的,带有大量的文档和图表。开发人员花在理解架构自己上的时间甚至超出了实现架构的时间。在前面的文章中,咱们提到了一些反对象牙塔式架构的一个缘由,而其中的一个缘由就是象牙塔式架构的设计者每每在设计时参杂进过多的自身经验,而不是严格的按照需求来进行设计。在软件开发领域,最为常见的设计就是"Code and Fix"方式的设计,设计随着软件开发过程而增加。或者,咱们能够认为这种方式根本就不能算是设计,它抱着一种船到桥头天然直的态度,但是在设计不断改动以后,代码变得臃肿且难以理解,处处充满着重复的代码。这样的情形下,架构的设计也就无从谈起,软件就像是在风雨中的破屋,濒临倒塌。针对于这种情形,新的设计方式又出现了,Martin Fowler称这种方式为"Planned Design"。和建筑的设计相似,它强调在编码以前进行严格的设计。这也就是咱们在团队设计中谈到的架构设计师的典型作法。设计师们一般不会去编程,理由是在土木工程中,你不可能看到一位设计师还要砌砖头。 "Planned Design"较之"Code and Fix"进步了许多,可是仍是会存在不少问题。除了在团队设计中咱们谈的问题以外,需求变动将会致使更大的麻烦。所以,咱们理所固然的想到进行"弹性设计":弹性的设计可以知足需求的变动。而弹性的设计所付出的代价就是复杂的设计。题外话:这里咱们谈论"Planned Design"引出的一些问题,并无任何排斥这种方式的意思。"Planned Design"仍是有不少可取之处的,但也有不少须要改进的地方。事实上,本文中咱们讨论的架构设计方式,本质上也是属于"Planned Design"方式。和"Planned Design"相对应的方式是XP所主张的"Evolutionary Design"方式,可是这种方式还有待于实践的检验,并不能简单的说他就必定要比"Planned Design"先进或落后。但能够确定的一点是:"Evolutionary Design"方式中有不少的思想和技巧是值得"Planned Design"借鉴的。 Solution XP中有两个很是响亮的口号:"Do The Simplest Thing that Could Possibly Work"和"You Aren't Going to Need It"(一般称之为YAGNI)。他们的核心思想就是不要为了考虑未来,把目前并不须要的功能加到软件中来。粗看之下,会有不少开发人员认为这是不切实际的口号。我能理解这种想法,其实,在我热衷于模式、可重用组件技术的时候,我对XP提倡的简单的口号嗤之以鼻。但在实际中,个人一些软件由于复杂设计致使开发成本上升的时候,我从新思考这个问题,发现简单的设计是有道理的。下降开发的成本不管是模式,可重用组件,或是框架技术,目的都是为了下降开发的成本。可是他们的方式是先进行大量的投入,而后再节省后续的开发成本。所以,架构设计方面的不少思路都是围绕着这种想法展开的,这可能也是致使开发人员广泛认为架构设计遥不可及的缘由。 XP的方式偏偏相反,在处理第一个问题的时候,没必要要也不可能就设计出具备弹性、近乎完美的架构来。这项工做应该是随着开发的演进,慢慢成熟起来的。我不敢说这种方式确定正确,可是若是咱们把生物的结构视同为架构,这种方式不是很相似于天然界中生物的进化方式吗?在一开始就制做出完美的架构的设想并无错,关键是很难作到这一点。老是会有不少的问题是你在作设计时没有考虑到的。这样,当一开始花费大量精力设计出的"天衣无缝"的架构必然会遇到意想不到的问题,这时候,复杂的架构反而会影响到设计的改进,致使开发成本的上升。这就比如若是方向错了,交通工具再快,反而致使错误的快速扩大。Martin Fowler在他的论文中说,"Working on the wrong solution early is even more wasteful than working on the right solution early"(提早作一件错事要比提早作一件对的事更浪费时间),相信也是这个道理。更有意思的是,一般咱们更有可能作错。在咱们进行架构设计的时候,咱们不可能彻底取得详细的需求。事实上,就算你已经取得了完整的需求,也有可能发生变化。这种状况下作出的架构设计是不可能不出错的。这样,浪费大量的时间在初始阶段设计不可能达到的"完美架构",倒不如把时间花在后续的改进上。提高沟通的效率咱们在团队设计中已经谈过了团队设计的目标之一就是为了下降沟通的成本,以期让全部人都可以理解架构。可是若是架构若是过于复杂,将会从新致使沟通成本的上升,并且,这个成本并不会随着项目进行而下降,反而会由于上面咱们提到的遇到新的问题致使沟通成本的持续上升。简单的架构设计能够加快开发团队理解架构的速度。咱们能够经过两种方式来理解简单的含义。首先,简单意味着问题的解不会很是的复杂,架构是解决需求的关键,不管需求再怎么复杂多变,老是能够找出简单稳定的部分,咱们能够把这个简单稳定的部分作为基础,再根据须要进行改进扩展,以解决复杂的问题。在示例中,咱们提到了measurement pattern,它就是按照这种想法来进行设计的。其次,简单性还体如今表示的简单上。一份5页的文档就可以表达清楚的架构设计为何要花费50页呢?一样的道理,可以用一副简单的图形就可以表示的架构设计也没有必要使用文档。毕竟,面对面的沟通才是最有效率的沟通,文档不论如何的复杂,都不能被彻底理解,并且,复杂的文档,维护起来也须要花费大量的时间。只有在两种状况下,咱们提倡使用复杂的文档:一是开发团队没有办法作到面对面沟通;二是开发成果要做为团队的知识积累起来,为下一次开发所用。考虑将来咱们之因此考虑将来,主要的缘由就是需求的不稳定。所以,咱们若是考虑将来可能发生的需求变化,就会不知觉的在架构设计中增长复杂的成分。这违背的简单的精神。可是,若是你不考虑可能出现的状况,那些和目前设计格格不入的改变,将会致使大量的返工。还记得YAGNI吗?原则上,咱们仍然坚持不要在现有的系统中为未来可能的状况进行设计。可是,咱们必须思考,必需要为未来可能出现的状况作一些准备。其实,软件中了不得的接口的思想,不就是源于此吗?所以,思考将来,但等到须要时再实现。变动案例有助于咱们思考将来,变动案例就是你在未来可能要(或可能不要)知足的,但如今不须要知足的需求。当咱们在作架构设计的时候,变动案例也将会成为设计的考虑因素之一,但它不可能成为进行决策的惟一考虑因素。不少的时候,咱们沉迷于设计通用系统给咱们带来的挑战之中,其实,咱们所作的工做对用户而言是毫无心义的。架构的稳定架构简单化和架构的稳定性有什么关系吗?咱们说,架构越简单,其稳定性就越好。理由很简单,1个拥有4个方法和3个属性的类,和1个拥有20个方法和30属性的类相比,哪个更稳定?固然是前者。而架构最终都是要映射到代码级别上的,所以架构的简单将会带来架构的稳定。尽量的让你的类小一些,尽量的让你的方法短一些,尽量的让类之间的关系少一些。这并非个人忠告,不少的设计类的文章都是这么说的。在这个话题上,咱们能够进一步的阅读同类的文章(关于 refactoring 的思考)。辨正的简单所以,对咱们来讲,简单的意义就是不要把将来的、或不须要实现的功能加入到目前的软件中,相应的架构设计也不须要考虑这些额外的需求,只要恰好可以知足当前的需求就行了。这就是简单的定义。但是在现实之中,老是有这样或者那样的缘由,使得设计趋向复杂。通常来讲,若是一个设计对团队而言是有价值的,那么,付出必定的成原本研究、验证、发展、文档化这个设计是有意义的。反之,若是一个设计没有很大的价值或是发展它的成本超过了其可以提供的价值,那就不须要去考虑这个设计。价值对不一样的团队来讲具备不一样的含义。有时候多是时间,有时候多是用户价值,有时候多是为了团队的设计积累和代码重用,有时候是为了得到经验,有时候是为了研究出可重用的框架(FrameWork)。这些也能够称为目的,所以,你在设计架构时,请注意先肯定好你的目的,对实现目的有帮助的事情才考虑。 Scott W.Ambler在他的文章中提到一个他亲身经历的故事,在软件开发的架构设计过程当中,花了不少的时间来设计数据库到业务逻辑的映射架构,虽然这是一件任何开发人员都乐意专研的事情(由于它很酷)。但他不得不认可,对用户来讲,这种设计先进的架构是没有太大的意义的,由于用户并不关心具体的技术。当看到这个故事的时候,个人触动很大。一个开发人员老是热衷于新奇的技术,可是若是这个新奇技术的成本由用户来承担,是否是合理呢?虽然新技术的采用可以为用户带来效益,可是没有人计算过效益背后的成本。就我开发过的项目而言,这个成本每每是大于效益的。这个问题可能并无肯定的答案,只能是见仁见智了。 简单并不等于实现简单说到这里,若是你们有一个误解,认为一个简单的架构也必定是容易设计的,那就错了。简单的架构并不等于实现起来也简单。简单的架构须要设计者花费大量的心血,也要求设计者对技术有很深的造诣。在咱们正在进行的一个项目中,一开始设计的基础架构在实现中被修改了几回,但每修改一次,代码量都减小一分,代码的可读性也就加强一分。从心理的角度上来讲,对本身的架构进行不断的修改,确实是须要必定的勇气的。由于不管是设计仍是代码,都是开发人员的心血。但跨出这一步是值得的。右侧的例子讨论了Java的IO设计,Java类库的设计应该来讲是很是优秀的,可是仍然避免不了从新的修改。实际上,在软件开发领域,因为原先的设计失误而致使后来设计过于复杂的状况比比皆是(例如微软的OLE)。一样的,咱们在设计软件的时候,也须要对设计进行不断的修改。可以实现复杂功能,同时自身又简单的设计并非一件容易的事情。简单设计须要什么样的设计师简单的架构须要全面的设计师。什么才是全面的设计师,个人定义是既可以设计,又可以编码。咱们在团队设计模式中就已经谈过象牙塔式架构和象牙塔式架构设计师。他们最容易犯的一个毛病就是设计和代码的脱离。从咱们本身的经验来看,即便在设计阶段考虑的很是完美的架构,在编码阶段也会出现这样或那样的问题。从而致使架构实现变得复杂。最明显的特征就是在编码时出现了有大量方法的类,或是方法很长的类。这代表架构和代码脱钩了。在咱们的开发过程当中,不仅一次出现这种现象,或者说,出现了坏味道(Bad Smell)。Refactoring的技巧也一样有助于识别坏味道。一次的架构设计完成后,开发人员能够按照设计,快速的编程。可在一段时间以后,新的特点不断的加入,咱们发现代码开始混乱,代码量增大,可读性降低,调试变得困难,代码不可控制的征兆开始出现。咱们就知道,架构的设计须要调整了。这属于咱们在后面所提到的Refactoring模式。而咱们在这里要说的是,若是架构的设计师不参与编码,它是没法感觉到坏味道的,所以也就不会主动的对设计进行改进。要解决这个问题,最好的办法是让设计师参与代码的编写,尤为是重要架构的现实部分须要设计师的参与。若是设计师没有办法参与编码,那就须要一种机制,可以把代码反馈给设计师,让他在适当的时候,从新考虑改进架构。一个可能的办法是Code Review。让设计师审核代码,以确保编码者真正了解了架构设计的意图。例1.Java的IO系统从Java的IO系统设计中,咱们能够感觉到简单设计的困难。 IO系统设计的困难性向来是公认的。Java的IO设计的一个目的就是使IO的使用简单化。在Java的1.0中,Java的IO系统主要是把IO系统分为输入输出两个大部分,并分别定义了抽象类InputStream和OutputStream。从这两个的抽象类出发,实现了一系列不一样功能的输入输出类,同时,Java的IO系统还在输入输出中实现了FilterInputStream和FilterOutputStream的抽象类以及相关的一系列实现,从而把不一样的功能的输入输出函数链接在一块儿,实现复杂的功能。这个实现实际上是Decorator模式(因为没有看过源码和相关的资料,这里仅仅是根据功能和使用技巧推测,若是你们有不一样的意见,欢迎来信讨论)。所以,咱们能够把多个对象叠加在一块儿,提供复杂的功能: DataInpuStream in = new DataInputStream( new BufferedInputStream( new FileInputStream("test.txt"); 上面的代码使用了两个FilterInputStream:DataInpuStream和BufferedInputStream,以实现读数据和缓冲的功能,同时使用了一个InputStream:FileInputStream,从文件中读取流数据。虽然使用起来不是很方便,可是应该仍是很是清晰的设计。令设计混乱的是既不属于InputStream,也不属于OutputStream的类,例如RandomAccessFile,这正代表,因为功能的复杂化,使得原先基于输入输出分类的设计变得混乱,根据咱们的经验,咱们说设计须要Refactoring了。所以,在Java1.1中,IO系统被从新设计,采用了Reader和Writer位基础的设计,并增长了新的特性。可是目前的设计彷佛更加混乱了,由于咱们须要同时使用1.0和1.1两种不一样的IO设计。 例2. measurement pattern 在分析模式一书中有一个measurement pattern(测量模式),原来它是为了要解决现实中各类各样纷繁复杂的可测量的属性。例如,一个医疗系统中,可能会有身高多高,体重多种,血压多少等上千种可测量的属性。若是分别表示它们,必然致使系统复杂性的上升。所以measurement pattern就从这些属性的可测量的共性出发,研究新的解决方法,提出了measurement pattern的想法:如图所示,把可测量的属性(Measurement)作为Phenomenon Type的实例,此外,每个的Person能够拥有多个的Measurement,同时,Measurement还对应处理的属性,例如图中的Quantity,就表示了Measurement的数量和单位。好比,一我的的体重是65公斤,那么,Phenomenon Type就是体重,Quantity的amount是65,units是公斤。图 5.牋 measurement pattern 的类图这实际上是一个很简单的设计,但它清楚的表示了属性之间的关系,简化了数千种的属性带来的复杂性。此外,咱们进一步思考,就会发现,这种架构只是针对目前出现属性众多的问题的基本解决方法,它还能够根据具体的须要进行扩展,例如,实现动态添加单位,或实现不一样单位的转化等问题。所以,咱们这里展现的实际上是一种思考的方法,假想一下,当你在面对一个复杂的医疗系统时,大量的属性和不一样的处理方式,你是否是能够从这样复杂的需求中找出简单的部分来呢?在咱们架构设计的第一篇中,咱们谈到架构设计的本质在于抽象,这里例子就是最典型的一个例子,在咱们传统的想法中,咱们都会把身高、体重等概念作为属性或是类,可是为了知足这里的需求,咱们对这些具体的概念作一个抽象,提出可测量类别的概念,并把它设计为类(Phenomenon Type),而把具体的概念作为实例。这种抽象的思想在软件设计中无处不在,例如元类的概念。更深刻的理解下一章中咱们将会讨论迭代设计,其中还会涉及到简单设计的相关知识。建议能够将两章的内容结合起来看。 迭代是一种软件开发的生命周期模型,在设计中应用迭代设计,咱们能够获得不少的好处。 Context 在软件生命周期中,咱们如何对待架构设计的发展? Problem 架构设计每每发生在细节需求还没有完成的时候进行的。所以,随着项目的进行,需求还可能细化,可能变动。原先的架构确定会有不足或错误的地方。那么,咱们应该如何对待原先的设计呢?咱们在简单设计模式中简单提到了"Planned Design"和"Evolutionary Design"的区别。XP社团的人们推崇使用"Evolutionary Design"的方式,在外人看来,彷佛拥护者们历来不须要架构的设计,他们采用的方式是一开始就进入代码的编写,而后用Refactoring来改进代码的质量,解决未经设计致使的代码质量低下的功能。从必定程度上来讲,这个观点并无错,它强调了代码对软件的重要性,并经过一些技巧(如Refactoring)来解决缺少设计的问题。但我并不认同"Evolutionary Design"的方式,在我看来,必定程度上的"Planned Design"是必须的,至少在中国的软件行业中,"Planned Design"尚未成为主要的设计方向。借用一句明言,"凡事预则立,不预则废",在软件设计初期,投入精力进行架构的设计是颇有必要的,这个架构是你在后续的设计、编码过程当中依赖的基础。可是,一开始咱们提到的设计改进的问题依然存在,咱们如何解决它呢?在简单设计模式中,咱们提到了设计改进的必要性,可是,若是没有一种方法去控制设计的改进的话,那么设计改进自己就是一场噩梦。所以,什么时候改进,怎么改进, 如何控制,这都是咱们须要面对的问题。 Solution 为了实现不断的改进,咱们将在开发流程中引入迭代的概念。迭代的概念在个人另外一篇文章--《需求的实践》中已经提到,这里咱们假设读者已经有了基本的迭代的概念。软件编码以前的工做大体能够分为这样一个工做流程:上图中的流程隐含着一个信息的损失的过程。来自于用户的需求通过整理以后,开发人员就会从中去掉一些信息,一样的事情发生在后面的过程当中,信息丢失或变形的状况不断的发生。这里发生了什么问题?应该说,需求信息的失真是很是广泛的,咱们缺乏的是一种有效的办法来抑止失真,换句话说,就是缺乏反馈。若是把眼睛蒙上,那咱们确定没有办法走出一条很长的直线。咱们走路的时候都是针对目标不断的调整本身的方向的。一样的,漫长的软件开发过程若是没有一种反馈机制来调整方向,那最后的软件真是不可思议。因此咱们引入了迭代周期。初始设计和迭代设计在团队设计中,咱们一直在强调,设计组最开始获得的设计必定只是一个原始架构,而后把这个原始架构传播到每一位开发者的手中,从而在开发团队中造成共同的愿景。(愿景(Vision):源自于管理学,表示将来的愿望和景象。这里借用来表示软件在开发人员心中的样子。在后面的文章中咱们会有一个章节专门的讨论架构愿景。)迭代(Iterate)设计,或者咱们称之为增量(Incremental)设计的思想和XP提倡的Evolutionary Design有殊途同归之妙。咱们能够从XP、Crystal、RUP、ClearRoom等方法学中对比、体会迭代设计的精妙之处:每一次的迭代都是在上一次迭代的基础上进行的,迭代将致力于重用、修改、加强目前的架构,以使架构愈来愈强壮。在软件生命周期的最后,咱们除了获得软件,还获得了一个很是稳定的架构。对于一个软件组织来讲,这个架构颇有可能就是下一个软件的投入或参考。咱们能够把早期的原始架构看成第一次迭代前的早期投入,也能够把它作为第一次迭代的重点,这些都是无所谓的。关键在于,原始架构对于后续的架构设计而言是很是重要的,咱们讨论过架构是来源于需求的,可是原始架构应该来源于那些比较稳定的需求。 TIP:现实中迭代设计退化为"Code and Fix"的设计的状况家常便饭("Code and Fix"参见简单设计)。从表面上看,二者的作法并无太大的差异,都是针对原有的设计进行改进。可是,两者效果的差异是明显的:"Code and Fix"是混沌的,毫无方向感可言,每一次的改进只是给原先就已风雨飘摇的积木上再加一块积木而已。而迭代设计的每一次改进都朝着一个稳定的目标在前进,他给开发人员带来信心,而不是打击。在过程上,咱们说迭代设计是在控制之下的。从实践的经验中,咱们发现,把原该在目前就该解决的问题退后是形成这一问题的主要缘由之一。所以,请严格的对待每一次的迭代,确保计划已经完成、确保软件的质量、确保用户的需求获得知足,这样才是正统的迭代之路。 单次的迭代咱们说,每一次的迭代实际上是一个完整的小过程。也就是说,它一样要经历文章中讨论的这些过程模式。只不过,这些模式的工做量都不大,你甚至能够在很短的时间内作完全部的事情。所以,咱们好像又回到了文章的开头,从新讨论架构设计的过程。单次迭代最令咱们兴奋的就是咱们老是能够获得一个在当前迭代中至关稳定的结果,而不像普通的架构设计那样,咱们深怕架构会出现问题,但又不得不依赖这个架构。从咱们的心理上来分析,咱们是在持续的建设架构中,咱们不须要回避需求的变动,由于咱们相信,在需求相对应的迭代中,咱们会继续对架构进行改进。你们不要认为这种心理的改变是可有可无的,我起初并无意识到这个问题,可是我很快发现新的架构设计过程仍然笼罩在原先的害怕改变的阴影之下的时候,迭代设计很容易就退化为"Code and Fix"的情形。开发人员难以接受新方法的主要缘由仍是在心理上。所以,我不得不花了不少的时间来和开发人员进行沟通,这就是我现实的经验。迭代的交错基于咱们对运筹学的一点经验,迭代设计之间确定不是线性的关系。这样说的一个缘由架构设计和后续的工做间仍是时间差的。所以,咱们不会傻到把时间浪费在等待其它工做上。通常而言,当下一次迭代的需求开始以后,详细需求开始以前,咱们就已经能够开始下一次迭代的架构设计了。各次迭代之间的时间距离要视项目的具体状况而定。好比,人员比较紧张的项目中,主要的架构设计人员可能也要担任编码人员的角色,下一次迭代的架构设计就可能要等到编码工做的高峰期过了以后。但是,屡次的交错迭代就可能产生版本的问题。好比,本次的迭代的编码中发现了架构的一个问题,反馈给架构设计组,可是架构设计组已经根据伪修改的本次迭代的架构开始了下一次迭代的架构设计,这时候就会出现不一样的设计之间的冲突问题。这种状况固然能够经过增强对设计模型的管理和引入版本控制机制来解决,但确定会随之带来管理成本上升的问题,而这是不符合敏捷的思想的。这时候,团队设计就体现了他的威力了,这也是咱们在团队设计中没有提到的一个缘由。团队设计经过彻底的沟通,能够解决架构设计中存在冲突的问题。 迭代频率 XP提倡迭代周期越短越好(XP建议为一到两周),这是个不错的提议。在这么短的一个迭代周期内,咱们花在架构设计上的时间可能就只有一两个小时到半天的时间。这时候,会有一个颇有意思的现象,你很难去区分架构设计和设计的概念了。由于在这么短的一个周期以内,完成的需求数量是不多的,可能就只有一两个用例或用户素材。所以,这几项需求的设计是否是属于架构设计呢?若是是的话,因为开发过程是由屡次的迭代组成的,那么开发过程当中的设计不都属于架构设计了吗?咱们说,架构是一个相对的概念,是针对范围而言的,在传统的瀑布模型中,咱们能够很容易的区分出架构设计和普通设计,若是咱们把一次迭代看做是一个单独的生命周期,那么,普通的设计在这样一个范围以内也就是架构设计,他们并无什么两样。可是,迭代周期中的架构设计是要遵循必定的原则的,这咱们在下面还会提到。咱们但愿迭代频率越快越好,可是这还要根据现实的状况而定。好比数据仓库项目,在项目的初期阶段,咱们不得不花费大量的时间来进行数据建模的工做,这其实也是一项专门针对数据的架构设计,创建元数据,制定维,整理数据,这样子的过程很难分为屡次的迭代周期来实现。如何肯定软件的迭代周期能够说,若是一支开发团队没有相关迭代的概念,那么这支团队要马上实现时隔两周迭代周期是很是困难的,,同时也是毫无心义的。就像咱们在上面讨论的,影响迭代周期的因素不少,以致于咱们那没法对迭代周期进行量化的定义。所以咱们只能从定性的角度分析迭代周期的发展。另外一个了解迭代的方法是阅读XP的相关资料,我认为XP中关于迭代周期的使用是很不错的一种方法,只是他强调的如此短的迭代周期对于不少的软件团队而言都是难以实现的。迭代周期的引入必定是一个从粗糙到精确的过程。迭代的本质实际上是短周期的计划,所以这也是迭代周期越短对咱们越有好处的一大缘由,由于时间缩短了,计划的可预测性就加强了。咱们知道,计划的制定是依赖于已往的经验,若是原先咱们没有制定计划或细节计划的经验,那么咱们的计划就必定是很是粗糙,最后的偏差也必定很大。可是这没有关系,每一次的计划都会对下一次的计划产生正面的影响,等到经验足够的时候,计划将会很是的精确,最后的偏差也会很小。迭代周期的肯定须要依赖于单位工做量。单位工做量指的是必定时间内你能够量化的最小的绩效。最简单的单位工做量是每位程序员一天的编码行数。惋惜显示每每比较残酷,团队中不但有程序员的角色,还有设计师、测试人员、文档制做人员等角色的存在,单纯的编码行数是不可以做为惟一的统计依据的。一样,只强调编码行数,也会致使其它的问题,例如代码质量。为了保证统计的合理性,比较好的作法是一个团队实现某个功能所花费的天数做为单位工做量。这里讨论的内容实际是软件测量技术,若是有机会的话,再和你们探讨这个问题。迭代周期和软件架构的改进咱们应用迭代方法的最大的目的就是为了稳步的改进软件架构。所以,咱们须要了解架构是如何在软件开发的过程当中不断演进的。在后面的文章中,咱们会谈到用Refactoring的方法来改进软件架构,可是Refactoring的定义中强调,Refactoring必须在不修改代码的外部功能的状况下进行。对于架构来讲,咱们能够近乎等价的认为就是在外部接口不变的状况下对架构进行改进。而在实际的开发中,除非很是有经验,不然在软件开发全过程当中保持全部的软件接口不变是一件很是困难的事情。所以,咱们这里谈的架构的改进虽然和Refactoring有相似之处,但仍是有区别的。软件架构的改进在软件开发过程会经历一个振荡期,这个振荡期可能横跨了数个迭代周期,其间架构的设计将会经历剧烈的变化,但最后必定会取向于平稳。(若是项目后期没有出现设计平稳化的状况,那么很不幸,你的项目注定要失败了,要么是时间的问题,要么就是需求的问题)。关键的问题在于,咱们有没有勇气,在架构须要改变的时候就毅然作出变化,而不是眼睁睁的看着问题变得愈来愈严重。最后的例子中,咱们讨论三个迭代周期,假设咱们在第二个周期的时候拒绝对架构进行改变,那么第三个周期必定是有如噩梦通常。变化,才有可能成功。咱们知道变化的重要性,但没有办法知道变化的确切时间。不过咱们能够从开发过程当中嗅到架构须要变化的气味:当程序中重复的代码逐渐变多的时候,当某些类变得格外的臃肿的时候,当编码人员的编码速度开始降低的时候,当需求出现大量的变更的时候。例子:从这一周开始,我和个人小组将要负责对软件项目中的表示层的设计。在这个迭代周期中,咱们的任务是要为客户端提供6到10个的视图。因为视图并不不少,表示层的架构设计很是的简单:准确的说,这里谈不上设计,只是简单让客户端访问不一样的视图而已。固然,在设计的示意图中,咱们并无必要画出全部的视图来,只要可以表达客户端和视图的关联性就能够了。(架构设计须要和具体的实现绑定,可是在这个例子中,为了着重体现设计的演进,所以把没必要要的信息都删掉。在实际的设计中,视图多是JSP页面,也多是一个窗口。)第一个迭代周的任务很快的完成了,小组负责的表示层模块也很顺利的和其它小组完成了对接,一个简陋但可以运转的小系统顺利的发布。客户观看了这个系统的演示,对系统提出了修改和补充。第二个迭代周中,模块要处理的视图增长到了30个,视图之间存在相同的部分,而且,负责数据层的小组对咱们说,因为客户需求的改进,同一个视图中将会出现不一样的数据源。因为咱们的视图中直接使用了数据层小组提供给咱们的数据源的函数,这意味着咱们的设计须要进行较大的调整。考虑到系统的视图的量大大的增长,咱们有必要对视图进行集中的管理。前端控制器(Front Control)模式将会是一个不错的技巧。对于视图之间的广泛的重复部分,能够将视图划分为不一样的子视图,再把子视图组合为各类各样的视图。这样咱们就能够使用组合(Composite)模式:客户的请求集中提交给控制器,控制器接受到客户的请求以后,根据必定的规则,来提供不一样的视图来反馈给客户。控制器是一个具备扩展能力的设计,目前的视图数量并很少,所以仍然能够使用控制器来直接分配视图。若是视图的处理规则比较复杂,咱们还能够使用建立工厂(Create Factory)模式来专门处理生成视图的问题。对于视图来讲,使用组合模式,把多个不一样数据源的视图组合为复杂的视图。
 
例如,一个JSP的页面中,可能须要分为头页面和尾页面。项目进入第三个迭代周期以后,表示层的需求进一步复杂化。咱们须要处理权限信息、须要处理数据的合法性判断、还须要面对更多的视图带来的复杂程度上升的问题。表示层的权限处理比较简单,咱们能够从前端控制器中增长权限控制的模块。同时,为了解决合法性判断问题,咱们又增长了一个数据过滤链模块,来完成数据的合法性判断和转换的工做。为了避免使得控制器部分的功能过于复杂,咱们把原先属于控制器的视图分发功能转移到新的分发器模块,而控制器专门负责用户请求、视图的控制。咱们来回顾这个例子,从迭代周期1中的需求最为简单,其实,现实中的项目刚开始的需求虽然未必会像例子中的那么简单,但必定不会过于复杂,所以迭代周期1的设计也很是的简单。到了迭代周期2的时候,需求开始变得复杂,按照原先的架构继续设计的话,必然会致使不少的问题,所以对架构进行改进是必要的。咱们看到,新的设计可以知足新的需求。一样的,迭代周期3的需求更加的复杂,所以设计也随之演进。这就是咱们在文章的开始提到的"Evolutionary Design"的演进的思想。

推荐书籍:
 
<script language='JavaScript'>eLong_Affiliate_MemberID=4053787;</script><script language="javascript" src="http://icons.elong.com/adjs/jd_jp/sw/13.js"></script>