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

2、架构设计的敏捷视图


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

  架构设计是一种权衡(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技术。

  那么,咱们应该如何避开这些误区呢,或者,换句话说,敏捷软件开发是如何作架构设计的。这里有几种过程模式:

185068.jpg

  图 2. 敏捷架构过程模式概览(High-Level)

  在接下去的篇幅中,咱们会逐一对各类过程模式进行介绍。而后再站在全局的角度分析各个模式之间的关系,并将之概括为架构设计的模式。

   敏捷型架构设计

  咱们说咱们这里列出的过程模式是敏捷型的,关于这一点咱们会在接下去的各个章节中验证这一点。咱们列出的各个过程模式并非彻底照搬敏捷型方法,由于在各类敏捷型方法中,某些技巧适合架构设计,某些方法则不适合架构设计。所以,咱们在采用一种方法和技术前,咱们会问本身几个简单的问题:

   ·该方法/技巧有什么价值?

   ·该方法/技巧须要多大的投入?从建立、维护、培训等多方面估计。

   ·比较该方法/技巧的投入和价值,它还值得咱们采用吗?

   ·是否还有其它价值/投入比更高的方法/技巧呢?   在咱们的文章中,每一种方法/技巧的讨论都回答了前三个问题,至于第四个问题,但愿有同行可以告诉我。