现在的位置: 首页 > 综合 > 正文

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

2014年01月02日 ⁄ 综合 ⁄ 共 7790字 ⁄ 字号 评论关闭

 

 

 

 

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


 

目录

1.从方法论看架构设计... 2

2.架构设计的敏捷视图... 7

3.源自需求... 13

4.团队设计... 18

5.简单设计... 24

6.迭代设计... 29

7.组合使用模式... 36

8.架构愿景... 41

9.分层 () 45

10.分层 () 53

11.精化和合并... 60

12Refactoring. 66

13.稳定化... 71

14.代码验证... 75

15.进一步阅读... 80

 


 

 

1.从方法论看架构设计

方法论对软件开发而言意味着什么?我们如何看待软件开发中的方法论?方法论能够成为软件开发的救命稻草吗?在读过此文后,这些疑惑就会得到解答。

在第一篇文章中,我们来了解标题中的一些词的含义。

  • 方法学是什么?

  • 敏捷是什么?

  • 为什么讨论架构?

方法论
方法论的英文为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)做为写作架构设计的形式,主要的原因就是模式是一种很好的组织思想的方法。

因此,在我们接下去的历程中,我们集中讨论的东西就围绕着架构、方法学、敏捷这三个要素展开。这篇文章并不是讨论如何编码实现软件架构的,也不要单纯的把它看作架构设计的指南,其实文中的很多思想来自于方法论,因此提到的很多架构设计的思想也适用于其它工作,如果能够了解这一点,看这篇文章的收获可能会更多一些。

2.架构设计的敏捷视图

通过上一章的介绍,我们对敏捷和方法有了一个大致的了解,从这一章起,我们开始对软件开发过程中架构设计的研究。记住一点,我们并不是为了架构设计而研究架构设计,我们的目的在于敏捷方法学的应用。

架构设计是一种权衡(trade-off)。一个问题总是有多种的解决方案。而我们要确定唯一的架构设计的解决方案,就意味着我们要在不同的矛盾体之间做出一个权衡。我们在设计的过程总是可以看到很多的矛盾体:开放和整合,一致性和特殊化,稳定性和延展性等等。任何一对矛盾体都源于我们对软件的不同期望。可是,要满足我们希望软件稳定运行的要求,就必然会影响我们对软件易于扩展的期望。我们希望软件简单明了,却增加了我们设计的复杂度。没有一个软件能够满足所有的要求,因为这些要求之间带有天生的互斥性。而我们评价架构设计的好坏的依据,就只能是根据不同要求的轻重缓急,在其间做出权衡的合理性。

目标
我们希望一个好的架构能够:

  • 重用:为了避免重复劳动,为了降低成本,我们希望能够重用之前的代码、之前的设计。重用是我们不断追求的目标之一,但事实上,做到这一点可没有那么容易。在现实中,人们已经在架构重用上做了很多的工作,工作的成果称为框架(Framework),比如说Windows的窗口机制、J2EE平台等。但是在企业商业建模方面,有效的框架还非常的少。

  • 透明:有些时候,我们为了提高效率,把实现的细节隐藏起来,仅把客户需求的接口呈现给客户。这样,具体的实现对客户来说就是透明的。一个具体的例子是我们使用JSP的tag技术来代替JSP的嵌入代码,因为我们的HTML界面人员更熟悉tag的方式。

  • 延展:我们对延展的渴求源于需求的易变。因此我们需要架构具有一定的延展性,以适应未来可能的变化。可是,如上所说,延展性和稳定性,延展性和简单性都是矛盾的。因此我们需要权衡我们的投入/产出比。以设计出具有适当和延展性的架构。

  • 简明:一个复杂的架构不论是测试还是维护都是困难的。我们希望架构能够在满足目的的情况下尽可能的简单明了。但是简单明了的含义究竟是什么好像并没有一个明确的定义。使用模式能够使设计变得简单,但这是建立在我熟悉设计模式的基础上。对于一个并不懂设计模式的人,他会认为这个架构很复杂。对于这种情况,我只能对他说,去看看设计模式。

  • 高效:不论是什么系统,我们都希望架构是高效的。这一点对于一些特定的系统来说尤其重要。例如实时系统、高访问量的网站。这些值的是技术上的高效,有时候我们指的高效是效益上的高效。例如,一个只有几十到一百访问量的信息系统,是不是有必要使用EJB技术,这就需要我们综合的评估效益了。

  • 安全:安全并不是我们文章讨论的重点,却是架构的一个很重要的方面。

规则
为了达到上述的目的,我们通常需要对架构设计制定一些简单的规则:

功能分解

顾名思义,就是把功能分解开来。为什么呢?我们之所以很难达到重用目标就是因为我们编写的程序经常处于一种好像是重复的功能,但又有轻微差别的状态中。我们很多时候就会经不住诱惑,用拷贝粘贴再做少量修改的方式完成一个功能。这种行为在XP中是坚决不被允许的。XP提倡"Once and only once",目的就是为了杜绝这种拷贝修改的现象。为了做到这一点,我们通常要把功能分解到细粒度。很多的设计思想都提倡小类,为的就是这个目的。

所以,我们的程序中的类和方法的数目就会大大增长,而每个类和方法的平均代码却会大大的下降。可是,我们怎么知道这个度应该要如何把握呢,关于这个疑问,并没有明确的答案,要看个人的功力和具体的要求,但是一般来说,我们可以用一个简单的动词短语来命名类或方法的,那就会是比较好的分类方法。

我们使用功能分解的规则,有助于提高重用性,因为我们每个类和方法的精度都提高了。这是符合大自然的原则的,我们研究自然的主要的一个方向就是将物质分解。我们的思路同样可以应用在软件开发上。除了重用性,功能分解还能实现透明的目标,因为我们使用了功能分解的规则之后,每个类都有自己的单独功能,这样,我们对一个类的研究就可以集中在这个类本身,而不用牵涉到过多的类。

根据实际情况决定不同类间的耦合度

虽然我们总是希望类间的耦合度比较低,但是我们必须客观的评价耦合度。系统之间不可能总是松耦合的,那样肯定什么也做不了。而我们决定耦合的程度的依据何在呢?简单的说,就是根据需求的稳定性,来决定耦合的程度。对于稳定性高的需求,不容易发生变化的需求,我们完全可以把各类设计成紧耦合的(我们虽然讨论类之间的耦合度,但其实功能块、模块、包之间的耦合度也是一样的),因为这样可以提高效率,而且我们还可以使用一些更好的技术来提高效率或简化代码,例如Java中的内部类技术。可是,如果需求极有可能变化,我们就需要充分的考虑类之间的耦合问题,我们可以想出各种各样的办法来降低耦合程度,但是归纳起来,不外乎增加抽象的层次来隔离不同的类,这个抽象层次可以是具体的类,也可以是接口,或是一组的类(例如Beans)。我们可以借用Java中的一句话来概括降低耦合度的思想:"针对接口编程,而不是针对实现编程。"

设计不同的耦合度有利于实现透明和延展。对于类的客户(调用者)来说,他不需要知道过多

抱歉!评论已关闭.