中间件

“需求确认”意味着什么

  在“需求分析报告”上签字确认,通常被认为是客户同意需求分析的标志行为,然而实际操作中,客户往往把“签字”看作是毫无意义的事情。“他们要我在需求文档的最后一行下面签名,于是我就签了,否则这些开发人员不开始编码。”

  这种态度将带来麻烦,譬如客户想更改需求或对产品不满时就会说:“不错,我是在需求分析报告上签了字,但我并没有时间去读完所有的内容,我是相信你们的,是你们非让我签字的。”

  同样问题也会发生在仅把“签字确认”看作是完成任务的分析人员身上,一旦有需求变更出现,他便指着“需求分析报告”说:“您已经在需求上签字了,所以这些就是我们所开发的,如果您想要别的什么,您应早些告诉我们。”

  这两种态度都是不对的。因为不可能在项目的早期就了解所有的需求,而且毫无疑问地需求将会出现变更,在“需求分析报告”上签字确认是终止需求分析过程的正确方法,所以我们必须明白签字意味着什么。

  对“需求分析报告”的签名是建立在一个需求协议的基线上,因此我们对签名应该这样理解:“我同意这份需求文档表述了我们对项目软件需求的了解,进一步的变更可在此基线上通过项目定义的变更过程来进行。我知道变更可能会使我们重新协商成本、资源和项目阶段任务等事宜。”对需求分析达成一定的共识会使双方易于忍受将来的摩擦,这些摩擦来源于项目的改进和需求的误差或市场和业务的新要求等。

  需求确认将迷雾拨散,显现需求的真面目,给初步的需求开发工作画上了双方都明确的句号,并有助于形成一个持续良好的客户与开发人员的关系,为项目的成功奠定了坚实的基础。
 软件开发是一项很复杂的工作,对于软件开发的管理和控制,现在有一门专门的学科:软件工程。在这方面有许多国家标准和国际标准。许多公司也有相应的文档模版,及相关规定。现在不谈技术角度来规范软件开发的管理和控制,从管理和实践的角度来探讨软件开发的管理和控制应遵循的的一些原则。
  对于软件开发项目中,经常出现两种极端情况,一种是创造了新的生产率和质量的纪录;一种则完全是一场灾难,不是被取消就是拖延很长时间。前者如在很短的时间内,为了赶进度,在几乎不可能的时间内开发出一套软件产品,创造了软件开发的记录,满足了上级所要求的上机日期,由于开发时间太短,过于仓促,上机时,问题百出,试运行时间长达几个月或一年半载的,而且程序一改再改,维护工作量大。后者,如某套系统未弄清楚需求,或因设计问题,开发失败。通过提炼这些成功和失败的例子,软件项目成功或失败的根本原因可能会更清晰一些。
  在讨论这些原因之前,我们先来说明一下什么情况可以称为失败的软件项目。
  1. 由于费用超支或计划执行超时而终止。
  2. 完成计划的时间或费用超过了原计划的50%。
  3. 由于质量或性能上的原因引起和客户的纠纷。

  下面我们将按其影响大小的顺序排列说明5种错误的实践方式。
  错误1:没有软件项目开发的历史数据
  缺乏软件开发的历史数据是大多数软件项目失败的关键所在, 这样的结论也许使很多人感到吃惊,但事实就是如此。没有一个可靠的软件开发的历史数据会使项目经理,程序员,客户对于软件开发的过程缺少清醒的认识。
假设现在你正在管理一个软件项目,而这个项目还没有一个公司在36个月内完成。作为一个负责的经理,你作了一个比较细致和保守的估计,然后告诉你的客户和你的手下说你认为这个项目需要36-38个月完成。然而常常有这样的情况发生:你的客户和程序员要求把时间压缩到18个月。客户一方面希望软件尽早投入使用而产生经济效益,一方面也想压缩项目时间作为一个讨价还价的筹码;而程序员一方面可能过于自信,一方面尽早结束项目也能使他们多赚点钱。而此时你的手头上也没有一个可靠的软件开发的历史数据,在他们的压力下你同意了18个月的计划,于是一场灾难开始了。
  在项目的开始阶段你发现计划被拖延了,于是开始向程序员们施加压力,要求他们加快进度,程序员为了追求进度而不得不把其它指标放在一边,这些问题不断的积累下来而项目经理却蒙在鼓里。到了项目中后期这些质量问题会不断暴露出来,而且互相关联并且难以解决,甚至有些是系统设计的问题,这时才发现好多模块要推倒重来,18个月完成计划变成了天方夜谭。虽然上面只是一个虚拟的例子,但在实际中这种情况比比皆是。问题的关键就在于软件开发的历史数据是反映软件开发队伍的能力的标尺,没有了这个标尺,就无法对软件的开发过程有一个清醒的认识。

  错误2:不重视使用软件费用估值工具软件和计划工具软件
  软件开发方法述评
  60年代中期开始爆发了众所周知的软件危机。为了克服这一危机,在1968、1969年连续召开的两次著名的NATO会议上提出了软件工程这一术语,并在以后不断发展、完善。与此同时,软件研究人员也在不断探索新的软件开发方法。至今已形成八类软件开发方法。

  一、Parnas方法
  最早的软件开发方法是由D.Parnas在1972年提出的。由于当时软件在可维护性和可靠性方面存在着严重问题,因此Parnas提出的方法是针对这两个问题的。首先,Parnas提出了信息隐蔽原则:在概要设计时列出将来可能发生变化的因素,并在模块划分时将这些因素放到个别模块的内部。这样,在将来由于这些因素变化而需修改软件时,只需修改这些个别的模块,其它模块不受影响。信息隐蔽技术不仅提高了软件的可维护性,而且也避免了错误的蔓延,改善了软件的可靠性。现在信息隐蔽原则已成为软件工程学中的一条重要原则。
  Parnas提出的第二条原则是在软件设计时应对可能发生的种种意外故障采取措施。软件是很脆弱的,很可能因为一个微小的错误而引发严重的事故,所以必须加强防范。如在分配使用设备前,应该取设备状态字,检查设备是否正常。此外,模块之间也要加强检查,防止错误蔓延。
  Parnas对软件开发提出了深刻的见解。遗憾的是,他没有给出明确的工作流程。所以这一方法不能独立使用,只能作为其它方法的补充。

  二、SASA方法
  1978年,E.Yourdon和L.L.Constantine提出了结构化方法,即SASD方法,也可称为面向功能的软件开发方法或面向数据流的软件开发方法。1979年TomDeMarco对此方法作了进一步的完善。
  Yourdon方法是80年代使用最广泛的软件开发方法。它首先用结构化分析(SA)对软件进行需求分析,然后用结构化设计(SD)方法进行总体设计,最后是结构化编程(SP)。这一方法不仅开发步骤明确,SA、SD、SP相辅相成,一气呵成,而且给出了两类典型的软件结构(变换型和事务型),便于参照,使软件开发的成功率大大提高,从而深受软件开发人员的青睐。

  三、面向数据结构的软件开发方法
  Jackson方法
  1975年,M.A.Jackson提出了一类至今仍广泛使用的软件开发方法。这一方法从目标系统的输入、输出数据结构入手,导出程序框架结构,再补充其它细节,就可得到完整的程序结构图。这一方法对输入、输出数据结构明确的中小型系统特别有效,如商业应用中的文件表格处理。该方法也可与其它方法结合,用于模块的详细设计。
Jackson方法有时也称为面向数据结构的软件设计方法。
  Warnier方法
  1974年,J.D.Warnier提出的软件开发方法与Jackson方法类似。差别有三点:一是它们使用的图形工具不同,分别使用Warnier图和Jackson图;另一个差别是使用的伪码不同;最主要的差别是在构造程序框架时,Warnier方法仅考虑输入数据结构,而Jackson方法不仅考虑输入数据结构,而且还考虑输出数据结构。

  四、问题分析法
  PAM问题分析法。PAM(ProblemAnalysisMethod)是80年代末由日立公司提出的一种软件开发方法。
  PAM方法希望能兼顾Yourdon方法、Jackson方法和自底向上的软件开发方法的优点,而避免它们的缺陷。它的基本思想是:考虑到输入、输出数据结构,指导系统的分解,在系统分析指导下逐步综合。这一方法的具体步骤是:从输入、输出数据结构导出基本处理框;分析这些处理框之间的先后关系;按先后关系逐步综合处理框,直到画出整个系统的PAD图。从上述步骤中可以看出,这一方法本质上是综合的自底向上的方法,但在逐步综合之前已进行了有目的的分解,这个目的就是充分考虑系统的输入、输出数据结构。
  PAM方法的另一个优点是使用PAD图。这是一种二维树形结构图,是到目前为止最好的详细设计表示方法之一,远远优于NS图和PDL语言。
  这一方法在日本较为流行,软件开发的成功率也很高。由于在输入、输出数据结构与整个系统之间同样存在着鸿沟,这一方法仍只适用于中小型问题。

  五、面向对象的软件开发方法
  面向对象技术是软件技术的一次革命,在软件开发史上具有里程碑的意义。
  随着OOP(面向对象编程)向OOD(面向对象设计)和OOA(面向对象分析)的发展,最终形成面向对象的软件开发方法OMT(LbjectModellingTechnique)。这是一种自底向上和自顶向下相结合的方法,而且它以对象建模为基础,从而不仅考虑了输入、输出数据结构,实际上也包含了所有对象的数据结构。所以OMT彻底实现了PAM没有完全实现的目标。不仅如此,OO技术在需求分析、可维护性和可靠性这三个软件开发的关键环节和质量指标上有了实质性的突破,彻底地解决了在这些方面存在的严重问题,从而宣告了软件危机末日的来临。
  自底向上的归纳
  OMT的第一步是从问题的陈述入手,构造系统模型。从真实系统导出类的体系,即对象模型包括类的属性,与子类、父类的继承关系,以及类之间的关联。类是具有相似属性和行为的一组具体实例(客观对象)的抽象,父类是若干子类的归纳。因此这是一种自底向上的归纳过程。在自底向上的归纳过程中,为使子类能更合理地继承父类的属性和行为,可能需要自顶向下的修改,从而使整个类体系更加合理。由于这种类体系的构造是从具体到抽象,再从抽象到具体,符合人类的思维规律,因此能更快、更方便地完成任务。这与自顶向下的Yourdon方法构成鲜明的对照。在Yourdon方法中构造系统模型是最困难的一步,因为自顶向下的"顶"是一个空中楼阁,缺乏坚实的基础,而且功能分解有相当大的任意性,因此需要开发人员有丰富的软件开发经验。而在OMT中这一工作可由一般开发人员较快地完成。在对象模型建立后,很容易在这一基础上再导出动态模型和功能模型。这三个模型一起构成要求解的系统模型。
  自顶向下的分解
  系统模型建立后的工作就是分解。与Yourdon方法按功能分解不同,在OMT中通常按服务(Service)来分解。服务是具有共同目标的相关功能的集合,如I/O处理、图形处理等。这一步的分解通常很明确,而这些子系统的进一步分解因有较具体的系统模型为依据,也相对容易。所以OMT也具有自顶向下方法的优点,即能有效地控制模块的复杂性,同时避免了Yourdon方法中功能分解的困难和不确定性。
  OMT的基础是对象模型
  每个对象类由数据结构(属性)和操作(行为)组成,有关的所有数据结构(包括输入、输出数据结构)都成了软件开发的依据。因此Jackson方法和PAM中输入、输出数据结构与整个系统之间的鸿沟在OMT中不再存在。OMT不仅具有Jackson方法和PAM的优点,而且可以应用于大型系统。更重要的是,在Jackson方法和PAM方法中,当它们的出发点--输入、输出数据结构(即系统的边界)发生变化时,整个软件必须推倒重来。但在OMT中系统边界的改变只是增加或减少一些对象而已,整个系统改动极小。
  需求分析彻底
  需求分析不彻底是软件失败的主要原因之一。即使在目前,这一危险依然存在。传统的软件开发方法不允许在开发过程中用户的需求发生变化,从而导致种种问题。正是由于这一原因,人们提出了原型化方法,推出探索原型、实验原型和进化原型,积极鼓励用户改进需求。在每次改进需求后又形成新的进化原型供用户试用,直到用户基本满意,大大提高了软件的成功率。但是它要求软件开发人员能迅速生成这些原型,这就要求有自动生成代码的工具的支持。
  OMT彻底解决了这一问题。因为需求分析过程已与系统模型的形成过程一致,开发人员与用户的讨论是从用户熟悉的具体实例(实体)开始的。开发人员必须搞清现实系统才能导出系统模型,这就使用户与开发人员之间有了共同的语言,避免了传统需求分析中可能产生的种种问题。
  可维护性大大改善
  在OMT之前的软件开发方法都是基于功能分解的。尽管软件工程学在可维护方面作出了极大的努力,使软件的可维护性有较大的改进。但从本质上讲,基于功能分解的软件是不易维护的。因为功能一旦有变化都会使开发的软件系统产生较大的变化,甚至推倒重来。更严重的是,在这种软件系统中,修改是困难的。由于种种原因,即使是微小的修改也可能引入新的错误。所以传统开发方法很可能会引起软件成本增长失控、软件质量得不到保证等一系列严重问题。正是OMT才使软件的可维护性有了质的改善。
  OMT的基础是目标系统的对象模型,而不是功能的分解。功能是对象的使用,它依赖于应用的细节,并在开发过程中不断变化。由于对象是客观存在的,因此当需求变化时对象的性质要比对象的使用更为稳定,从而使建立在对象结构上的软件系统也更为稳定。
  更重要的是OMT彻底解决了软件的可维护性。在OO语言中,子类不仅可以继承父类的属性和行为,而且也可以重载父类的某个行为(虚函数)。利用这一特点,我们可以方便地进行功能修改:引入某类的一个子类,对要修改的一些行为(即虚函数或虚方法)进行重载,也就是对它们重新定义。由于不再在原来的程序模块中引入修改,所以彻底解决了软件的可修改性,从而也彻底解决了软件的可维护性。OO技术还提高了软件的可靠性和健壮性。

  六、可视化开发方法
  可视化开发是90年代软件界最大的两个热点之一。随着图形用户界面的兴起,用户界面在软件系统中所占的比例也越来越大,有的甚至高达60~70%。产生这一问题的原因是图形界面元素的生成很不方便。为此Windows提供了应用程序设计接口API(Application Programming Interface),它包含了600多个函数,极大地方便了图形用户界面的开发。但是在这批函数中,大量的函数参数和使用数量更多的有关常量,使基于Windows API的开发变得相当困难。为此Borland C++推出了Object Windows编程。它将API的各部分用对象类进行封装,提供了大量预定义的类,并为这些定义了许多成员函数。利用子类对父类的继承性,以及实例对类的函数的引用,应用程序的开发可以省却大量类的定义,省却大量成员函数的定义或只需作少量修改以定义子类。
  Object Windows还提供了许多标准的缺省处理,大大减少了应用程序开发的工作量。但要掌握它们,对非专业人员来说仍是一个沉重的负担。为此人们利用Windows API或Borland C++的Object Windows开发了一批可视开发工具。
  可视化开发就是在可视开发工具提供的图形用户界面上,通过操作界面元素,诸如菜单、按钮、对话框、编辑框、单选框、复选框、列表框和滚动条等,由可视开发工具自动生成应用软件。
  这类应用软件的工作方式是事件驱动。对每一事件,由系统产生相应的消息,再传递给相应的消息响应函数。这些消息响应函数是由可视开发工具在生成软件时自动装入的。
  国内的软件公司大多数是处在"十几条枪,一个手工作坊"的水平上,在承接软件开发的项目之后往往是几位骨干人物讨论之后对费用和进度作一个大致的估计,然后就开始进入项目的执行。这种方法带有明显的主观性。在作一个精确的软件费用估计和作一个比较现实的项目开发计划时需要考虑许多因素。对于一个大的软件项目,用手工作费用估计和作计划是不能胜任的。现在国外市场上有大约50种商业软件费用估计工具包和大约100种商业项目计划工具包,使用他们作精确的估计比手工的估计更可能获得成功。常用的软件费用估计工具软件有Checkpoint,Colomo,Estimacs,Price_s,Slim。 常用的项目管理软件有MS Project,Primavera,Project Manager*s Workbench,Timeline。把这二种工具软件联合使用可以互为补充,帮助经理驳回客户和程序员的无理要求并且能精确的控制项目的执行。

  错误3:忽视用户的需求的变动
  尽管最初的用户需求在签定开发合同时已经包含在需求说明书中,但在整个开发周期中期望用户的需求一直保持不变是不大可能的,因为用户对于如何应用计算机软件并没有一个成熟的经验。在项目进行中用户的需求会不断的增长,一般情况下用户的需求以每月1%的速率增加,如果一个项目在12个月内完成,最终将有超过10%的改动,如果项目要持续36个月,最后将增加1/3的功能。每月1%也只是一个经验数据,一个缺乏计算机应用经验的用户会更频繁的改变和增加他的要求。因此在作项目的费用和时间估计时一定要考虑用户需求的变化。一种比较明智的方法是在签定开发合同时把用户需求的改动和经济利益挂钩,如果用户增加或改动了需求,那么软件的交付日期可以推迟,费用也应增加。

  错误4:忽视监督项目的进度  
  到目前为止,软件产业还没有一个标准的项目进度的检查标准。一个比较清晰的尺度是用已经实现的软件功能反映项目的进度。但这种方法是否就是最科学的衡量标准,现在还不能定论,毕竟在一个软件项目中软件功能只是一个主要而非全部的任务。因此一个项目经理在监控项目执行时不应该只关注实现的软件功能,还要关心文档,测试,技术支持这些因素。在实际工作中我们经常听到经理或程序员说这样的话:"项目已经完成了90%",这种结论带有明显的主观性,一个优秀的项目经理不应该被手下的判断所迷惑,而应该按照一个比较客观的标准去深入检查。

  错误5:忽视设计复查和代码复查
  很多程序员习惯于这样一种工作方式:只做不想。他们更关心每天可以写多少行代码,完成几个模块。在这种态度下,他们都很不愿意复查自己的工作,而习惯于在软件测试阶段把隐藏的错误改正过来。但设计复查和代码复查在大型的软件项目中已经有30年的应用历史,而且已经被证明在设计和代码编写阶段的复查比软件测试更能有效的消除错误,一些经验数据表明,在设计和代码复查时发现的错误是在同等工作量下软件测试发现的错误的两倍。

  结论:
  软件开发是一个带有一定风险的工作,为了把风险降到最低, 在项目的执行中项目经理必须严格的监督项目的进度,对程序员不愿复查的坏习惯要给予纠正。项目经理必须要从软件开发的历史数据和辅助工具包提供的数据中作出精确的估计,在作估计时他应该考虑为不断变化的用户需求留出富余量。
方法论对软件开发而言意味着什么?我们如何看待软件开发中的方法论?方法论能够成为软件开发的救命稻草吗?在读过此文后,这些疑惑就会得到解答。在第一篇文章中,我们来了解标题中的一些词的含义。方法学是什么? 敏捷是什么? 为什么讨论架构?方法论方法论的英文为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个方面来考虑: 1、明确目标 泛泛的召开架构讨论会议是没有什么意义的,一个没有鲜明主题的会议也不会有什么结果。在源自需求的模式中,我们谈到说可以有非功能需求的架构,可以有功能需求的架构。因此,在进行团队设计之前,我们首先也需要确定,此次要解决什么问题,是讨论业务逻辑的架构,还是技术架构;是全局性的架构,还是各模块的架构。 2、明确分工 我们之所以重视团队,很重要的额一个原因就是不同的成员有不同的擅长的区域。有些成员可能擅长于业务逻辑的建模,有的擅长于原型设计,有的擅长于数据库设计,有的则擅长于Web编程。你能够想象一个软件没有界面吗?(有些软件可能是这种情况)你能够想象一个软件只有数据库,而没有处理逻辑吗?因此,架构设计就需要综合的考虑各个方面,充分利用成员的优势。这就要求团队的各个成员都能够明确自己的分工。 3、明确责权除了明确自己的分工,每位成员都需要清楚自己的责任。没有责任,分工就不会有任何的效力。每位成员都需要明确自己要做些什么。当然,和责任相对的,没有成员还需要知道自己的权力是什么。这些清楚了,进行高效的沟通的前提就具备了。每次架构的讨论下来,每个人都清楚,自己要做些什么,自己需要要求其他人做些什么,自己该对谁负责。如果这些问题回答不了,那这次的讨论就白费了。 4、明确沟通方式这里使用沟通方式可能有一点点不恰当,为了明确的表达意思,大家可以考虑信息流这个词。一个完整架构包括几个方面,分别都由那些人负责,如何产生,产生的整个过程应该是什么样的?这样的一个信息流程,囊括了上面提到的三个明确。如果团队的每一个人都能够为架构的产生而努力,并顺利的设计出架构,那么这样的流程是完美的。如果你发现其中的一些人不知道做些什么,那么,这就是流程出问题的现象了。完美的流程还会有一个额外的副产品,架构产生之后,团队对于软件的设计已经是非常的清晰了。因为我们提倡的是尽可能多的开发人员参与架构的设计。不仅仅是架构讨论到这里,其实有很多的内容已经脱离了架构设计了。也就是说,很多的原则和技巧都是可以用于软件开发的其它活动的。至于哪一些活动能够利用这些方法呢?大家可以结合自己的实际情况,来思考这个问题。提示一点,关键的入手处在于目前效率较低之处。 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"的演进的思想。

各位来宾、各位领导下午好,很高兴有机会在这里跟各位一起交流一下在软件技术和软件未来发展方面的一些体会。我报告的主要内容包括这样几部分。第一就是回顾一下我们目前软件和网络技术发展的情况。第二是对当前网络技术和产业发展工作的一些体会。最后介绍一下我们在这方面的工作。我们大家都很清楚,在最近十年来由于互联网巨大的发展和技术的进步,使得在网上已汇集了很多的计算资源,数据资源以及软件资源和各种数字化的设备。正像前面孙院士讲的信息化带动工业化,以后走向企业信息化后,各种制造业在互联网上形成了一种所谓的虚拟社会。之所以互联网能够给我们带来如此大的机遇其实也由于互联网几个主要的特点。第一就是所谓互联网的分布性,互联网的分布性为我们整个系统的发展和规模带来了机会。而它的开放性和动态性为我们系统的应用和各种方式的接入也提供了很多的机会。但与此同时,由于网络的分布性、开放性和动态性,也为软件技术和产业的发展创造了更多的机遇和带来了更多的挑战。在最新的福布斯的预测,信息技术在下一个大的浪潮中,也许从明年或者后年将会出现。这种新的浪潮的出现也会造就今后十五年网络技术、软件技术新的一轮的黄金时代。在这样一个背景下基于互联网的信息系统也会从三年前的一万亿美元发展成为20万亿美元。这样一个大的背景,技术发展和对产业的驱动力应该说是相当大的,而这样一个驱动力后面最重要的背景就是我们如何从过去计算机的互联、网页的互联到信息资源的互联。我们现在称之为网格计算。


简单来看传统一台PC机我们都清楚其内部五个单元的基本结构,未来的计算机都是联在网络中,将这五部分无限扩大后,形成了我们整个建立在虚拟社会中的一台巨大的计算机,而这台计算机是分布使用的,是动态的,也是开放的。建立在这样的环境下,我们的生产,我们的社会可以建立起一个基于开放协议的虚拟生产组织,真正适合我们所谓的在任何时间,任何地方获取我们所需要的服务。因此按需服务成为今后在软件业中重要的部分。应该讲随着网络快速的发展,这种虚拟的网络社会给我们在软件产业发展和网络技术方面会带来无限的发展。正是因为这样的发展趋势和强大的市场驱动力,在九十年代中期,美国、欧盟都向后实施了有关在下一代互联网以及在软件、网络安全方面的重大计划,各国政府尤其在发达国家以及一些发展中国家,都提出了一些优惠的政策和强大的支持力度来把网络软件作为在今后一段时期内软件技术,软件产业的中心和竞争焦点。


我们看国际的跨国公司,从微软的.NET,到IBM的旧金山计划,SUN的J2EE等等,所有这些都是为把战略注意力和产业技术的发展聚焦在网络的中心。也就是说未来的战争,将从平台中心战转为网络中心战,而今后的信息产业也将进一步面向网络的应用,服务和产品。它会把我们所有计算资源、数据资源、软件资源还有其他各种数字化设备共同够成一个所谓的虚拟网络社会。七十年代独立商软件出现。八十年代大众软件出现,到九十年代开放源码软件,共享软件体伴随互联网兴起,copyleft概念建立。现在网络化软件趋势以第三代中间件为代表的软件租赁业、软件服务业、网络化的软件成为当前最具挑战性的技术产业方向。未来网格计算将给我们提供更广阔的空间。


从产品来看我们说有这样四类,第一类就是我们常说的系统软件,第二类是最近市场空间比较小,但发展速度极快的中间件,第三是嵌入式系统开发类,最后一类是面向大型应用服务类的。我们的系统软件、开发技术、产业化程度的确有很大发展的空间,也存在着一些客观、具体的问题,但是面对我们信息化带动工业化、通过信息化实现装备现代化的大目标下,和我们现在日常生活、生产之间密切联系,实际上也有很多新的机会。我们国家最近几年兴起的关于电子商务,电子政务以及面向科学计算大平台E-Seience都是面向未来互联网应用这样的前提下的历史机遇。在这样的历史机遇,我们回顾过去信息技术的发展,从简单的以主机计算到客户服务器到近几年强势发展的客户网格计算,在这样的客户网格计算下,我们重新看待四大门类的软件产业,作为中间件应该是目前软件发展的重要契机,而网格计算是未来软件技术和软件产业发展的机遇。也是新一轮竞争的制高点。如何把握我们软件发展的战略转折点?实际上通过几年的努力,我们在关键软件和技术方面已经有了我们自己的一些版权。在软件产业方面也形成了一定的规模和结果,在一些行业也确实存在跨越式发展的机遇。但是面对互联网的发展,面对网络应用的需求如何把握时机就变成我们当前一个非常关键的问题。在互联网应用中其实主要的一个特点从早期面向对象的程式设计,到构件、组建化的设计,以及面向网络内部系统集成的一些Server包括后来的开放式互联网协议,为信息系统的集成和建设,以及数据共享互通互操作都提供了非常重要的机遇。所以从应用的范围来讲,WebServer开放互联网协议为我们当前的技术和产业的发展提供了重要的基础,也是现在一个主流的方向。从应用系统的偶合程度来说,基于WebServer使用了跨边界应用集成和高端技术。简单说中间件在没有操作系统前每做一个应用有一些共性的基础内容,从CPU管理、内存管理等等,从基本的特征完全一些应用开发。后来把这样一些问题分布在操作系统,提供了一个管理基础。进一步人们又开发了数字管理软件,批处理软件等等一系列构建在操作系统之上服务、应用于系统。这就是早期中间间的初步逻辑。在互联网的应用中,由于基本的基础设施,各个独立的应用网系统通过高速的传输建立了直接的联系,在操作系统和系统软件之上,形成了我们一个个应用的基础目标。但是为了构造我们所谓的电子商务,我们需要解决在上下游供应厂商、客户关系等等一系列的问题。这里可以抽象出一些通讯的中间件、安全处理以及一些事故处理。在电子政务当中涉及到跨部门、跨地区以及跨行业甚至一个领域内跨部门的集成合作。在其他的含量行业信息化当中也存在类似这样的问题。因此面向互联网的应用我们仍然面对大量共性的、支持面对关键业务服务的一些应用基础软件。我们把电子政务内容作为面向领域应用,面向互联网应用的基础应用。把它提炼为作为中间件的品牌,面向动态业务的支撑部分。在这种情况下,互联网的应用对中间件的技术、中间件产业提出了很高的要求。有公司预测在今后基于互联网的电子商务系统,将有将近80%的应用系统做到基于中间件的开发。而在2000年的中间件中收入达到了177亿美元,2000年、2001年IBM总裁讲过IBM(收入)主要来自于中间件和它的服务。而中间件占到整个软件收入的90%以上。IDC曾经预测,去年整个电子商务市场是超过1万亿。我们的赛迪也曾经预测,对于今后中国软件在中间件的发展速度。我们看在去年世界软件五百强中所快速发展的效果。从总的来说,互联网的发展和中间件的发展以需求和应用为依托。因此中间件产品和技术正在得到快速的发展,虽然到目前还没有形成像系统软件、大型应用软件这样一个市场份额。但是它的发展速度目前已超过任何一类传统软件的发展,已经形成了一个很好的发展趋势。同时由于应用的多样性、业务的广泛性,目前在中间件方面还没有形成垄断的产品,这也是我们开展中间件研究和产业化推进中的一个重要的契机。中间件技术是一种战略必争的技术,对网络的发展是至关重要的。这方面十五863计划已重点部署研发和应用。我们基本的设计是通过国产操作系统,在这样一个跨平台系统下实现中间件产品的系列。透过这样的核心技术体系和产业化的转移和国内重要的企业建立战略合作伙伴和产业化的应用推广。通过中间件的应用进一步支持整个在信息化建设中的应用需求,并且通过软件专业孵化器和核心技术的支持和管理来对我们应用提供基本的服务。


第二方面作为未来的发展,由于计算和通讯技术的和提出的所谓网格计算。在今后发展的大规模的虚拟环境下,可信的计算环境,就形成今后技术发展中非常重要,而且值得关注的一个研究方向。从技术的角度看今后的发展,因为网络计算的深层背景是值得我们思考的。而且另一我们假设看计算技术是否会向物理学系发展?因为我们从网络的动力学定义,网络的量子信息学都是为今后的研究提供一个机遇。从研究来看,我们知道以前的理论基础主要来自图灵模型,在逻辑学主要是来自歌德尔定理。值得注意的是近几年在Scienc、Nature等物理学家的关于互联网的论文。从应用的角度可以感受出服务的多样性,业务的关键性对整个网络提出的要求。因此在科学问题方面也向我们提出了两个研究的挑战,第一是如何构建和给出大规模网络的物理模型和计算环境。第二个问题,由于网络的飞速发展和行业应用信息化巨大的需求,如何建立高效、可靠的软件开发或者说软件生产的基础。这样的软件科学问题实际上也是我们在软件技术发展方面重要的因素。在这样的背景下,无论电子商务、其互联网的应用平台中都有其特殊的和共性的一些要求。但从电子政务来讲,我们现在的应用面临两个基本的问题。第一如何解决七国八制的数据和应用集成。第二个问题是安全策略和标准化问题。如何实现在跨边界,不同领域,多个系统的组合,由一个个单独的服务在不同的系统平台下通过偶合程度形成新的不同的系统,在一个自定义内构成一个完整的应用平台。同时透过跨行业、跨地区的不同自治域内的形成所谓弱偶合项互联网的集成,是我们在实际应用建设中必须面临的基础问题。在这样的前提下我们看整个互联网的软件,在基础服务项,我们软件的运行是平的。面向应用领域,透过中间件和质量保证技术提供所谓的面向一定领域的软件什么平台。比如说现在运行当中有各种各样的构建技术支持。通过组合和调度服务形成我们软件的平台,通过业务标准通过软件开发构成我们的软件基础,最后将我们所谓的用户和安全体系形成我们面向互联网应用的一个应用软件设计环境。在这样的设计环境中要解决的重要问题是过程技术。这样的平台下如何解决跨边界下信息孤岛的问题,避免信息应用中的独立性,形成面向互联网资源服务,是面向互联网重要的基础和核心软件。根据这样一些工作,我们北航也开展了一些在Web Server中间件平台以支持互联网、电子政务、电子商务的应用。这个平台经过我们五年的工作,现在已正在推向实际运用,并取得了比较好的效果。第二部分,就是面向领域的过程软件,我们针对面向过程的软件提出了新的方法,我们申请了两项专利和16个版权,在一定程度下实现了应用开发的零编程,而且将应用开发提炼成四个基本要素,解决了快速开发和维护的问题。


上面我通过软件技术和网络技术的发展跟各位一起交流。面对我国信息化建设和软件产业发展最好的时机,如何能够规划好、建设好并获取它的服务,能够提升我们软件产业整个的发展空间是我们目前关注的一个重要的问题。如何实现我们软件技术和产业的发展,也是我们国家在整个高技术发展当中一个重要的内容。希望我国软件技术和产业的发展越走越好,谢谢各位。
九十年代初客户机/服务器计算模式开始成为主流技术,将数据统一存储在数据服务器上,而有关的业务逻辑都在客户端实现,即所谓胖终端的解决方案,这种两层结构的模式大大阻碍着系统的发展,单一的服务器结构紧密地依赖供应商;数据存取受到限制;难以扩展到大企业广域网或国际互联网;也难以管理客户端的机群。随着用户业务需求的增长及Internet/Intranet的普及,将以三层或四层体系结构取而代之。三层结构就是把用户端的业务逻辑独立出来,并与数据库服务器中存储过程合并在一起,构成应用层,以提高计算能力,实现灵活性。在这种结构中用户端仅仅是处理图形用户界面(GUI),而目前趋势是采用具有交互功能的浏览器,即形成瘦终端的工作方式,为此,中间又增加了一层,称为Web服务器层,形成了四层体系结构。


这类多层结构的分布系统,各服务器和终端机之间都是通过网络连接起来的,并有大量信息和数据进行传递。对每个应用系统而言,在设计和实现时需要开发的,仅是在应用服务器上的业务逻辑部分的软件,除此之外,还必须要设计处理分布系统所特有的功能的软件,而目前的系统软件(操作系统和支撑软件)都不支持。为此出现了中间件,它是处于系统软件和应用软件之间的一批软件。使设计者集中设计与应用有关的部分,大大简化了设计和维护工作。通过五、六年的大量应用和实践,中间件已有一批成熟的产品,并成为设计分布系统时不可缺少的软件。仅仅几年时间,中间件以迅雷不及掩耳的速度迅猛发展,每年的市场销售额均是成倍的增长。


什么是中间件

中间件是处于操作系统和应用程序之间的软件,也有人认为它应该属于操作系统中的一部分。人们在使用中间件时,往往是一组中间件集成在一起,构成一个平台(包括开发平台和运行平台),但在这组中间件中必需要有一个通信中间件,因此,我同意这种定义:


中间件=平台+通信


这个定义也限定了只有用于分布式系统中才能称为中间件,同时还可以把它与支撑软件和实用软件区分开来。


中间件的作用和分类

要说清这个问题我们用一个生活中的实例来比喻。把分布式系统看作北京市区的交通系统,网络看作市区马路,通过交通工具(汽车)实现通信,每分钟将有几万辆车在马路上行驶,如果没有相应的交通设施和管理规划,北京市将会乱成一团,发生各种交通事故。


1.通信处理(消息)中间件

首先要修好马路,安装红绿灯,设立交通管理机构,制定出交通规则,也就是我们要建网和制定出通信协议,能在不同平台之间通信,实现分布式系统中可靠的、高效的、实时的跨平台数据传输(如TongLINK、BEA eLink 、IBM的MQSeries等),称为消息中间件。这是中间件中唯一不可缺少的,是销售额最大的中间件产品,目前在Windows 2000操作系统中已包含了其部分功能。


2.事务处理(交易)中间件

在分布式事务处理系统中要处理大量事务,常常在系统中要同时做上万笔事务。例如在北京市就要设置各种运载汽车,完成日常的运载,同时要随时监视汽车运行,出现故障时,要有排除措施,发生堵塞时要进行调度。在联机事务处理系统(OLTP)中,每笔事务常常要多台服务器上的程序顺序地协调完成,一旦中间发生某种故障时,不但要完成恢复工作,而且要自动切换系统,达到系统永不停机,实现高可靠性运行;同时要使大量事务在多台应用服务器能实时并发运行,并进行负载平衡地调度,实现昂贵的可靠性机和大型计算机系统同等的功能,为了实现这个目标,要求系统具有监视和调度整个系统的功能。BEA的 Tuxedo由此而著名,它成为增长率最高的厂商。一个事务处理平台,根据X/OPEN的参数模型规定,应由事务处理中间件、通信处理中间件以及数据存取管理中间件三部分组成。东方通科技公司的TongLINK 和TongEASY实现了这个参考模型规定。


3.数据存取管理中间件

在分布式系统中,重要的数据都集中存放在数据服务器中,它们可以是关系型的、复合文档型、具有各种存放格式的多媒体型,或者是经过加密或压缩存放的,该中间件将为在网络上虚拟缓冲存取、格式转换、解压等带来方便。


4.Web服务器中间件

浏览器图形用户界面已成为公认规范,然而它的会话能力差、不能作数据写入、受HTTP协议的限制等,就必需进行修改和扩充,形成了 Web服务器中间件,如 SilverStream公司的产品。


5.安全中间件

一些军事、政府和商务部门上网的最大障碍是安全保密问题,而且不能使用国外提供的安全措施(如防火墙、加密、认证等),必需用国产的产品。产生不安全因素是由操作系统引起的,但必需要用中间件去解决,以适应灵活多变的要求。


6.跨平台和构架的中间件

当前开发大型应用软件通常采用基于构架和构件技术,在分布系统中,还需要集成各节点上的不同系统平台上的构件或新老版本的构件,由此产生了构架中间件,功能最强的是CORBA,可以跨任意平台,但是太庞大;JavaBeans较灵活简单,很适合于做浏览器,但运行效率差;DCOM模型主要适合 Windows平台,已广泛使用。实际上国内新建系统主要是UNIX(包括LINUX)和 Windows,因此针对这两个平台建立相应的中间件要实用得多。


7.专用平台中间件

为特定应用领域设计领域参考模式,建立相应构架,配置相应的构件库和中间件,为应用服务器开发和运行特定领域的关键任务(如电子商务、网站等)。


8.网络中间件

它包括网管、接入、网络测试、虚拟社区、虚拟缓冲等,也是当前最热门的研发项目。


中间件模型和形态

中间件从诞生起,仅仅是五年时间,但发展极其迅速,是有史以来发展最快的软件产品,但在技术上还处于成长阶段,还没有统一的标准和模型,通常都是用C++语言以面向对象的技术来实现的,但是它的特性已超出面向对象的表达能力,由于它属于可重用构件,目前趋向于用构件技术来实现。然而中间件要涉及软件的所有标准、规范和技术,它含有更多的内涵,因为它包括平台功能,自身具有自治性、自主性、隔离性、社会化、激发性、主动性、并发性、认识能力等特性,是近似于Agent(代理)的结构,采用Agent的概念和技术更合适一些,建立模型和规范是促进技术发展的核心措施。


仲萃豪:中国科学院软件研究所研究员,博士生导师,研究方向是计算机软件与计算方法,仲老是我国第一代软件专家,曾率先研究软件中各领域课题:编译程序、语言、操作系统、应用软件等。近10年来主要从事应用软件开发及其软件工程研究。曾先后任10多所院校的兼职教授、北京市软件专业顾问、软件工程国家工程师研究中心研究员,并获得国家科技进步二等奖、省部级一等奖等8项。


消息中间件产品

IBM MQSeries


IBM MQSeries是IBM的消息处理中间件。MQSeries提供一个具有工业标准、安全、可靠的消息传输系统。


它的功能是控制和管理一个集成的系统,使得组成这个系统的多个分支应用(模块)之间通过传递消息完成整个工作流程。MQSeries基本由一个信息传输系统和一个应用程序接口组成,其资源是消息和队列。


MQSeries的关键功能之一是确保信息可靠传输,即使在网络通信不可靠或出现异常时也能保证信息的传输。MQSeries的异步消息处理技术能够保证当网络或者通信应用程序本身处于“忙"状态或发生故障时,系统之间的信息不会丢失,也不会阻塞。这样的可靠性是非常关键的,否则大量的金钱和客户信誉就会面临极大的损害。


同时,MQSeries是灵活的应用程序通信方案。MQSeries支持所有的主要计算平台和通信模式,也能够支持先进的技术(如Internet和Java),拥有连接至主要产品(如Lotus Notes和SAP/R3等)的接口。


东方通科技 TongLINK/Q


TongLINK/Q是面向消息的中间件。TongLINK/Q的主要功能是在不同的网络协议、不同的操作系统和不同的应用程序之间提供可靠的消息传送。


高效、可靠、灵活的传输功能

通过预建连接、多路复用、流量控制、压缩传输、断点重传、传输优先级管理、服务(类)驱动等机制来保证实现。

 

事件代理机制

事件代理机制提供了一种异步应用开发模型,用户只需要定义一个事件及怎么处理,TongLINK/Q自动完成操作。利用事件代理机制,可以实现事件订阅与发布,实现策略管理。

 

会话管理

TongLINK/Q通过一个简单的会话标识来描述一种复杂的通信关系,实现了更高层次、更抽象的通信服务。

 

良好的易用性和可管理性

TongLINK/Q实现了实时监控和管理,提供了日志机制、动态配置、远程管理功能。并提供多层次安全管理,支持多种开发工具。


交易中间件

BEA Tuxedo


BEA Tuxedo作为电子商务交易平台,它允许客户机和服务器参与一个涉及多个数据库协调更新的交易,并能够确保数据的完整性。BEA Tuxedo一个特色功能是能够保证对电子商务应用系统的不间断访问。它可以对系统组件进行持续的监视,查看是否有应用系统、交易、网络及硬件的故障。一旦出现故障,BEA Tuxedo会从逻辑上把故障组件排除,然后进行必要的恢复性步骤。


BEA Tuxedo根据系统的负载指示,自动开启和关闭应用服务,可以均衡所有可用系统的负载,以满足对应用系统的高强度使用需求。借助DDR(数据依赖路由),BEA Tuxedo可按照消息的上下文来选择消息路由。其交易队列功能,可使分布式应用系统以异步“少连接”方式协同工作。BEA Tuxedo LLE安全机制可确保用户数据的保密性,应用/交易管理接口(ATMI)为50多种硬件平台和操作系统提供了一致的应用编程接口。BEA Tuxedo基于网络的图形界面管理可以简化对电子商务的管理,为建立和部署电子商务应用系统提供了端到端的电子商务交易平台。


东方通科技TongEASY


TongEASY是面向交易处理的中间件。负责正确传递交易,对交易完整性进行管理,调度应用程序的运行,保证整个系统运行的高效性。TongEASY把自己的交易管理功能和数据库的交易管理功能有机的结合在一起,实现对分布式交易的全局管理。


交易管理机制是TongEASY最具有独创性的方面。TongEASY有两种方式管理交易完整性:一种方式是应用程序处理提交/撤销命令,另一种方式是建立在XA协议基础上的。基于XA的交易管理方式又可以细分为标准的XA方式和快速XA两种。


TongEASY在交易管理上的多样性是为了解决中国应用环境的特殊性。国内多数应用是建立在一个速率传输偏低、可靠性差强人意的广域网上。XA方式是建立在两阶段提交协议上的,在一个低速的广域网上使用两阶段提交协议时会带来一些问题,一是握手次数多、数据库加锁时间长,因而效率较低,二是不能很好地解决由于网络故障所造成的交易完整性问题。因此,TongEASY在交易管理方面提供了多种选择,以求得效率、交易完整性、开发工作量等关键指标的上佳表现。

posted on 2006-04-27 13:12  Sanle  阅读(696)  评论(1编辑  收藏  举报

导航