大家应该对这两个词很熟悉了,但是对词里包含的意义可能并不是特别清楚。首先必须说明的是,程序员和系统分析员不存在谁高级谁低级的分别,他们是两种职业,对职业技能的要求完全不同。所以厉害的程序员就是系统分析员的说法是不对的。当然,系统分析员的技能要求他必须要懂得如何写程序,但是他的重心在于如何把一个很大的项目切割成适合个人的小块,然后将这些小块组织起来。程序员的职责就是如何更好更快的实现这些小块。
在正式开始之前,我们还是来看在Thinking In Java中作者对分析和设计的一段精辟见解:
分析和设计
面向对象的范式是思考程序设计时一种新的、而且全然不同的方式,许多人最开始都会在如何构造一个项目上皱起了眉头。事实上,我们可以作出一个“好”的设计,它能充分利用OOP提供的所有优点。
请原谅在这里突然出现了OOP这个词,他的意思是面相对象,虽然在之前没有提到,但是在现在OO概念满天飞的软件世界里,大家应该对他不会太陌生。这里我简要的说明一下。在之前我介绍的实际上都是在很早以前程序写作流传下来的经验(什么,教我们老古董,打他!),但是以前的非OO(就是基于过程)的软件设计方法目前在国际上已经很少采用,所以我这里讲软件设计的时候所有的概念都是基于OO的。即使OO的概念很简单的啦,大家思考一下,我们再学习C++的时候一开始使用的类不都是一些动物啦、正方形啦之类的,都是生活中的例子,对吧。其实OO就是我们看世界的一种方式。可是最早由于计算机技术的不发达,我们不得不用一些很奇怪的描述来表达我们的意思,只有这样计算机才能理解,很笨不是吗。比如我们必须使用参数、过程、函数。所以当时的软件设计方法都是基于过程的。举一个简单的例子来显示OO设计方法和基于过程的设计方法之间的差别:一句简单的日常短语--“我吃饭”,用OO的方法来表述还是“我吃饭”,可是如果用基于过程的方法来描述的话就变成“我吃饭(饭)”,是不是很别扭呢。
有关OOP分析与设计的书籍大多数都不尽如人意。其中的大多数书都充斥着莫名其妙的话语、笨拙的笔调以及许多听起来似乎很重要的声明。我认为这种书最好压缩到一章左右的空间,至多写成一本非常薄的书。具有讽剌意味的是,那些特别专注于复杂事物管理的人往往在写一些浅显、明白的书上面大费周章!如果不能说得简单和直接,一定没多少人喜欢看这方面的内容。毕竟,OOP的全部宗旨就是让软件开发的过程变得更加容易。尽管这可能影响了那些喜欢解决复杂问题的人的生计,但为什么不从一开始就把事情弄得简单些呢?因此,希望我能从开始就为大家打下一个良好的基础,尽可能用几个段落来说清楚分析与设计的问题。
不要迷失
在整个开发过程中,最重要的事情就是:不要将自己迷失!但事实上这种事情很容易发生。大多数方法都设计用来解决最大范围内的问题。当然,也存在一些特别困难的项目,需要作者付出更为艰辛的努力,或者付出更大的代价。但是,大多数项目都是比较“常规”的,所以一般都能作出成功的分析与设计,而且只需用到推荐的一小部分方法。但无论多么有限,某些形式的处理总是有益的,这可使整个项目的开发更加容易,总比直接了当开始编码好! 也就是说,假如你正在考察一种特殊的方法,其中包含了大量细节,并推荐了许多步骤和文档,那么仍然很难正确判断自己该在何时停止。时刻提醒自己注意以下几个问题:
(1) 对象是什么?(怎样将自己的项目分割成一系列单独的组件?)
(2) 它们的接口是什么?(需要将什么消息发给每一个对象?)
在确定了对象和它们的接口后,便可着手编写一个程序。出于对多方面原因的考虑,可能还需要比这更多的说明及文档,但要求掌握的资料绝对不能比这还少。
整个过程可划分为四个阶段,阶段0刚刚开始采用某些形式的结构。
阶段0:拟出一个计划
第一步是决定在后面的过程中采取哪些步骤。这听起来似乎很简单(事实上,我们这儿说的一切都似乎很简单),但很常见的一种情况是:有些人甚至没有进入阶段1,便忙忙慌慌地开始编写代码。如果你的计划本来就是“直接开始开始编码”,那样做当然也无可非议(若对自己要解决的问题已有很透彻的理解,便可考虑那样做)。但最低程度也应同意自己该有个计划。
在这个阶段,可能要决定一些必要的附加处理结构。但非常不幸,有些程序员写程序时喜欢随心所欲,他们认为“该完成的时候自然会完成”。这样做刚开始可能不会有什么问题,但我觉得假如能在整个过程中设置几个标志,或者“路标”,将更有益于你集中注意力。这恐怕比单纯地为了“完成工作”而工作好得多。至少,在达到了一个又一个的目标,经过了一个接一个的路标以后,可对自己的进度有清晰的把握,干劲也会相应地提高,不会产生“路遥漫漫无期”的感觉。
从我刚开始学习故事结构起(我想有一天能写本小说出来),就一直坚持这种做法,感觉就象简单地让文字“流”到纸上。在我写与计算机有关的东西时,发现结构要比小说简单得多,所以不需要考虑太多这方面的问题。但我仍然制订了整个写作的结构,使自己对要写什么做到心中有数。因此,即使你的计划就是直接开始写程序,仍然需要经历以下的阶段,同时向自己提出一些特定的问题。
阶段1:要制作什么?
在上一代程序设计中(即“过程化或程序化设计”),这个阶段称为“建立需求分析和系统规格”。当然,那些操作今天已经不再需要了,或者至少改换了形式。大量令人头痛的文档资料已成为历史。但当时的初衷是好的。需求分析的意思是“建立一系列规则,根据它判断任务什么时候完成,以及客户怎样才能满意”。系统规格则表示“这里是一些具体的说明,让你知道程序需要做什么(而不是怎样做)才能满足要求”。
需求分析实际就是你和客户之间的一份合约(即使客户就在本公司内部工作,或者是其他对象及系统)。系统规格是对所面临问题的最高级别的一种揭示,我们依据它判断任务是否完成,以及需要花多长的时间。由于这些都需要取得参与者的一致同意,所以我建议尽可能地简化它们——最好采用列表和基本图表的形式——以节省时间。可能还会面临另一些限制,需要把它们扩充成为更大的文档。 我们特别要注意将重点放在这一阶段的核心问题上,不要纠缠于细枝末节。这个核心问题就是:决定采用什么系统。对这个问题,最有价值的工具就是一个名为“使用条件”的集合。对那些采用“假如……,系统该怎样做?”形式的问题,这便是最有说服力的回答。例如,“假如客户需要提取一张现金支票,但当时又没有这么多的现金储备,那么自动取款机该怎样反应?”对这个问题,“使用条件”可以指示自动取款机在那种“条件”下的正确操作。
应尽可能总结出自己系统的一套完整的“使用条件”或者“应用场合”。一旦完成这个工作,就相当于摸清了想让系统完成的核心任务。由于将重点放在“使用条件”上,一个很好的效果就是它们总能让你放精力放在最关键的东西上,并防止自己分心于对完成任务关系不大的其他事情上面。也就是说,只要掌握了一套完整的“使用条件”,就可以对自己的系统作出清晰的描述,并转移到下一个阶段。在这一阶段,也有可能无法完全掌握系统日后的各种应用场合,但这也没有关系。只要肯花时间,所有问题都会自然而然暴露出来。不要过份在意系统规格的“完美”,否则也容易产生挫败感和焦燥情绪。 在这一阶段,最好用几个简单的段落对自己的系统作出描述,然后围绕它们再进行扩充,添加一些“名词”和“动词”。“名词”自然成为对象,而“动词”自然成为要整合到对象接口中的“方法”。只要亲自试着做一做,就会发现这是多么有用的一个工具;有些时候,它能帮助你完成绝大多数的工作。
尽管仍处在初级阶段,但这时的一些日程安排也可能会非常管用。我们现在对自己要构建的东西应该有了一个较全面的认识,所以可能已经感觉到了它大概会花多长的时间来完成。此时要考虑多方面的因素:如果估计出一个较长的日程,那么公司也许决定不再继续下去;或者一名主管已经估算出了这个项目要花多长的时间,并会试着影响你的估计。但无论如何,最好从一开始就草拟出一份“诚实”的时间表,以后再进行一些暂时难以作出的决策。目前有许多技术可帮助我们计算出准确的日程安排(就象那些预测股票市场起落的技术),但通常最好的方法还是依赖自己的经验和直觉(不要忘记,直觉也要建立在经验上)。感觉一下大概需要花多长的时间,然后将这个时间加倍,再加上10%。你的感觉可能是正确的;“也许”能在那个时间里完成。但“加倍”使那个时间更加充裕,“10%”的时间则用于进行最后的推敲和深化。但同时也要对此向上级主管作出适当的解释,无论对方有什么抱怨和修改,只要明确地告诉他们:这样的一个日程安排,只是我的一个估计!
阶段2:如何构建?
在这一阶段,必须拿出一套设计方案,并解释其中包含的各类对象在外观上是什么样子,以及相互间是如何沟通的。此时可考虑采用一种特殊的图表工具:“统一建模语言”(UML)。请到http://www.rational.com/去下载一份UML规格书。作为第1阶段中的描述工具,UML也是很有帮助的。此外,还可用它在第2阶段中处理一些图表(如流程图)。当然并非一定要使用UML,但它对你会很有帮助,特别是在希望描绘一张详尽的图表,让许多人在一起研究的时候。除UML外,还可选择对对象以及它们的接口进行文字化描述(《Thinking in C++》里说的那样,但这种方法非常原始,发挥的作用亦较有限。
我曾有一次非常成功的咨询经历,那时涉及到一小组人的初始设计。他们以前还没有构建过OOP(面向对象程序设计)项目,将对象画在白板上面。我们谈到各对象相互间该如何沟通(通信),并删除了其中的一部分,以及替换了另一部分对象。这个小组(他们知道这个项目的目的是什么)实际上已经制订出了设计方案;他们自己“拥有”了设计,而不是让设计自然而然地显露出来。我在那里做的事情就是对设计进行指导,提出一些适当的问题,尝试作出一些假设,并从小组中得到反馈,以便修改那些假设。这个过程中最美妙的事情就是整个小组并不是通过学习一些抽象的例子来进行面向对象的设计,而是通过实践一个真正的设计来掌握OOP的窍门,而那个设计正是他们当时手上的工作!
作出了对对象以及它们的接口的说明后,就完成了第2阶段的工作。当然,这些工作可能并不完全。有些工作可能要等到进入阶段3才能得知。但这已经足够了。我们真正需要关心的是最终找出所有的对象。能早些发现当然好,但OOP提供了足够完美的结构,以后再找出它们也不迟。
阶段3:开始创建
读这本书的可能是程序员,现在进入的正是你可能最感兴趣的阶段。由于手头上有一个计划——无论它有多么简要,而且在正式编码前掌握了正确的设计结构,所以会发现接下去的工作比一开始就埋头写程序要简单得多。而这正是我们想达到的目的。让代码做到我们想做的事情,这是所有程序项目最终的目标。但切不要急功冒进,否则只有得不偿失。根据我的经验,最后先拿出一套较为全面的方案,使其尽可能设想周全,能满足尽可能多的要求。给我的感觉,编程更象一门艺术,不能只是作为技术活来看待。所有付出最终都会得到回报。作为真正的程序员,这并非可有可无的一种素质。全面的思考、周密的准备、良好的构造不仅使程序更易构建与调试,也使其更易理解和维护,而那正是一套软件赢利的必要条件。
构建好系统,并令其运行起来后,必须进行实际检验,以前做的那些需求分析和系统规格便可派上用场了。全面地考察自己的程序,确定提出的所有要求均已满足。现在一切似乎都该结束了?是吗?
阶段4:校订
事实上,整个开发周期还没有结束,现在进入的是传统意义上称为“维护”的一个阶段。“维护”是一个比较暧昧的称呼,可用它表示从“保持它按设想的轨道运行”、“加入客户从前忘了声明的功能”或者更传统的“除掉暴露出来的一切臭虫”等等意思。所以大家对“维护”这个词产生了许多误解,有的人认为:凡是需要“维护”的东西,必定不是好的,或者是有缺陷的!因为这个词说明你实际构建的是一个非常“原始”的程序,以后需要频繁地作出改动、添加新的代码或者防止它的落后、退化等。因此,我们需要用一个更合理的词语来称呼以后需要继续的工作。 这个词便是“校订”。换言之,“你第一次做的东西并不完善,所以需为自己留下一个深入学习、认知的空间,再回过头去作一些改变”。对于要解决的问题,随着对它的学习和了解愈加深入,可能需要作出大量改动。进行这些工作的一个动力是随着不断的改革优化,终于能够从自己的努力中得到回报,无论这需要经历一个较短还是较长的时期。
什么时候才叫“达到理想的状态”呢?这并不仅仅意味着程序必须按要求的那样工作,并能适应各种指定的“使用条件”,它也意味着代码的内部结构应当尽善尽美。至少,我们应能感觉出整个结构都能良好地协调运作。没有笨拙的语法,没有臃肿的对象,也没有一些华而不实的东西。除此以外,必须保证程序结构有很强的生命力。由于多方面的原因,以后对程序的改动是必不可少。但必须确定改动能够方便和清楚地进行。这里没有花巧可言。不仅需要理解自己构建的是什么,也要理解程序如何不断地进化。幸运的是,面向对象的程序设计语言特别适合进行这类连续作出的修改——由对象建立起来的边界可有效保证结构的整体性,并能防范对无关对象进行的无谓干扰、破坏。也可以对自己的程序作一些看似激烈的大变动,同时不会破坏程序的整体性,不会波及到其他代码。事实上,对“校订”的支持是OOP非常重要的一个特点。
通过校订,可创建出至少接近自己设想的东西。然后从整体上观察自己的作品,把它与自己的要求比较,看看还短缺什么。然后就可以从容地回过头去,对程序中不恰当的部分进行重新设计和重新实现(注释⑩)。在最终得到一套恰当的方案之前,可能需要解决一些不能回避的问题,或者至少解决问题的一个方面。而且一般要多“校订”几次才行。
构建一套系统时,“校订”几乎是不可避免的。我们需要不断地对比自己的需求,了解系统是否自己实际所需要的。有时只有实际看到系统,才能意识到自己需要解决一个不同的问题。若认为这种形式的校订必然会发生,那么最好尽快拿出自己的第一个版本,检查它是否自己希望的,使自己的思想不断趋向成熟。
反复的“校订”同“递增开发”有关密不可分的关系。递增开发意味着先从系统的核心入手,将其作为一个框架实现,以后要在这个框架的基础上逐渐建立起系统剩余的部分。随后,将准备提供的各种功能(特性)一个接一个地加入其中。这里最考验技巧的是架设起一个能方便扩充所有目标特性的一个框架(对这个问题,大家可参考第16章的论述)。这样做的好处在于一旦令核心框架运作起来,要加入的每一项特性就象它自身内的一个小项目,而非大项目的一部分。此外,开发或维护阶段合成的新特性可以更方便地加入。OOP之所以提供了对递增开发的支持,是由于假如程序设计得好,每一次递增都可以成为完善的对象或者对象组。
这有点类似“快速造型”。此时应着眼于建立一个简单、明了的版本,使自己能对系统有个清楚的把握。再把这个原型扔掉,并正式地构建一个。快速造型最麻烦的一种情况就是人们不将原型扔掉,而是直接在它的基础上建造。如果再加上程序化设计中“结构”的缺乏,就会导致一个混乱的系统,致使维护成本增加。
计划的回报
如果没有仔细拟定的设计图,当然不可能建起一所房子。如建立的是一所狗舍,尽管设计图可以不必那么详尽,但仍然需要一些草图,以做到心中有数。软件开发则完全不同,它的“设计图”(计划)必须详尽而完备。在很长的一段时间里,人们在他们的开发过程中并没有太多的结构,但那些大型项目很容易就会遭致失败。通过不断的摸索,人们掌握了数量众多的结构和详细资料。但它们的使用却使人提心吊胆在意——似乎需要把自己的大多数时间花在编写文档上,而没有多少时间来编程(经常如此)。我希望这里为大家讲述的一切能提供一条折衷的道路。需要采取一种最适合自己需要(以及习惯)的方法。不管制订出的计划有多么小,但与完全没有计划相比,一些形式的计划会极大改善你的项目。请记住:根据估计,没有计划的50%以上的项目都会失败!
非常佩服作者对软件构建过程的精辟见解,软件工程是一门内容非常繁杂的学科,但是作者能够用浅显易懂的句子把它描述出来,真的是非常不简单。软件工程最早的提出者并不是计算机的专业人士,而是一位建筑设计师,所以软件工程的很多思想来自于建筑学。经过了几十年的发展,软件工程经历了很多次的蜕变。形成了今天的世界上以一些大公司提出的架构为主的形式:比如微软提出的COM及COM+以及基于其上的DNA体系,SUN提出的EJB,CORBA,还有BEA、WebLogic、IBM等公司的架构。虽然架构有不同,但是他们的思想都是相通的,架构的作用都是起到辅助开发者实现规范的、科学的软件开发过程。至于谈软件项目的管理和开发,那么Rational公司就是这方面的鼻祖。综合来说,目前世界范围内的软件工程提倡的就是以渐进的、螺旋式的开发方法构建基于组件的软件产品。现在说这些东西可能有些画饼的嫌疑,随着我们专题讨论的继续深入,这些概念就会很清晰的展现在面前。
虽然很希望能够继续的讨论软件工程方面的东东,但是我们的这个专题毕竟是讨论如何编写优美的程序的,离题还是不要太过分的好,至于软件工程的详细讨论,我会在接下去的专题中继续。在接下去的篇幅中,我们会继续讨论程序员和系统分析员之间的差别。
四个阶段
这里我不想举一大堆的数字和实例来描述软件危机和论证软件工程的重要性,这方面的资料有很多,如果一一列举的话,会被怀疑别有用心。事实上,建造狗舍和写一个小的软件没有很大的区别。虽然你认为你的能力可以很轻松的完成小型的软件系统,根本不需要任何的计划。好!我来问问你,你在写程序代码的时候,有没有过漏这漏那,程序快接近完成的时候却发现少了一个很重要的模块;有没有过在书写了大量的代码之后觉得自己写出来的东西不堪入目,恨不得重头开始;有没有过写程序花了两天的时间,但是Debug却花了一个星期的时间;有没有过听到软件的使用者说要改需求,你就恨不得狠狠揍他一顿。如果都没有,那么只有两种可能:你是个超级天才,所有人类能够想到的美好品质你都具有,另一种可能:你根本没有开发过软件。
即便是个人开发的软件,软件工程科学中也有相应的方法来指导软件的开发过程,这种方法叫做PSP(个人软件开发过程),与此相对的,还有TSP(小组软件开发过程)。这些被事实证明行之有效的方法包括了一整套的规范,帮助你开发你的软件,不让你的程序变得无法控制。可以说,对于任何一个软件系统来说,只要你花一些时间去设计,即便你的设计仅仅只是在草稿纸上随便的涂抹,在软件开发完成后,你就会惊喜的发现,你在软件开发早期的小小投入,已经为你带来了额外的好处。
路标和RUP
在阶段0中,我想最重要的思想就是就是“路标”的概念了,和这个概念相类似的概念还有“周期”和“里程碑”的概念,这些概念在Rational公司的RUP(Rational Unified Process 软件统一过程)中有详细的论述。不论这些概念叫做什么,他们体现出来的是一种迭代开发的思想。面对当今的复杂的软件系统,使用连续的开发方法:如首先定义整个问题,设计完整的解决方案,编制软件并最终测试产品,是不可能的。需要一种能够通过一系列细化,若干个渐进的反复过程而生成有效解决方案的迭代方法。
Rational Unified Process支持专注于处理生命周期中每个阶段中最高风险的迭代开发方法,极大地减少了项目的风险性。迭代方法通过可验证的方法来帮助减少风险--经常性的,可执行版本使最终用户不断的介入和反馈。因为每个迭代过程以可执行版本告终,开发队伍停留在产生结果上,频繁的状态检查帮助确保项目能按时进行。迭代化方法同样使得需求、特色、日程上战略性的变化更为容易。(出自《Rational Unified Process白皮书》)
上面这段好像很复杂,但是他所要说明的思想却是很简单的,就拿搭建狗舍来说,你的第一个“路标”可能是要搭一个框架,这个框架是由几根结实的木头组成,等到框架完成之后,你会把你的小白叫来,让他试一下,糟糕的是,这个框架对于小白来说小了一些,这时候你嘘了一口气,因为你原来是打算把整个狗舍搭好以后再叫小白来试一下的,如果你那样做的话,你剩下的木头可能就不够再盖一间狗舍了。好吧,既然有了些问题,我们就把框架调整一下,可能这个过程也花了你一些木头,不过所幸木头还够。在修整完毕后,你觉得第一步的计划虽然有些挫折,不过仍可以算是成功的,接下来你就要建立第二个“路标”了:第二个的“路标”是为狗舍钉上墙板和做出一个底座,你可能花了一些时间来思考以及和你的小白商量是否要在墙壁上开一个窗户和给底座加上轮子,在决定之后,你很快的达成了第二个“路标”。而且在经过了小白的测试后,你发现完全没有问题,你自己都觉得有些佩服自己了,很快的,你又完成刷油漆等“路标”。整个过程进展的非常顺利,而你在做狗舍方面很有天赋的名声也在你的街坊四邻间不胫而走。
很简单是吧,其实本来就是简单的,软件工程的目的就是要把复杂的软件开发过程条理化,简单化。记住,在你使用迭代开发方法的时候,它在每个周期后的产品是一份可执行代码,是一份可以让你的用户品头论足的东西。而这份可执行代码不仅包括了程序本身,可能还有其他的产成品,例如:文档等。
问题和场景
在阶段1中,非常重要的一点是问题描述,在多数情况下,问题描述来自于你的软件的使用者,就是用户。用户的需求决定了问题描述,糟糕的是,用户多半不懂计算机,对他们来说,他们只能够用日常的语言来表达自己的需要。而你的任务就是要把他们的语言翻译成计算机语言,不过并不是指象C那样的高级语言,而是便于你构造系统的需求描述语言。这同样很简单:你只需要问自己几个问题就可以:在什么场合?有什么条件?做些什么事?回答好这三个问题,你就完成了一个完整的问题描述了。
举一个简单的例子:一个银行的信贷系统有这样的问题描述:
“若顾客采取信用贷款方式,销售员就请求信用部门的审核人员查核顾客的信用,此时审核人员会向销售员取得顾客信用编号和销货总金额。”
在某种条件下应该做什么事情,这就是这个问题描述的表现形式,很简单是吧。
实际上,这里可以引申出两个概念:场景(context)和问题(problem),场景指的是一种特定的情况,会导致某种问题的发生;而问题是在某个场景之中,但它也有可能产生出新的场景。
过程和对象
有必要说明一下以前基于过程的软件开发和目前基于对象的软件开发的不同。在没有OO的年代里,DFD(Data Flow Diagram 数据流程图)是一份软件设计中的非常重要的文档,注意力的关键也是集中在数据如何在各个系统之间传递。可是在现在的OO概念中,数据大有为消息(message)所替代的趋势。比如你到麦当劳快餐店,要花10块钱买一份汉堡。在DFD的时代,就是这样表示的:
如果你用消息表示法来表示话,就是另一种方式:
怎么样,你觉得那一种方式更自然呢。(如果你敢回答第一种的话我就...)。再比如上一段话中关于问题描述的例子,如果用传统的数据描述的方法的话,就会是这样子的:
“若采取信用贷款方式,销售员就将顾客信用编号及总金额交给信用部门的信用审核人员。”
请比较其中的两句话:
“若采取信用贷款方式,销售员就将顾客信用编号及总金额交给信用部门的信用审核人员。”
“若顾客采取信用贷款方式,销售员就请求信用部门的审核人员查核顾客的信用,此时审核人员会向销售员取得顾客信用编号和销货总金额。”
了解其中的不同之处了吗,用自然的语言去描述你的问题,这是写出好的软件的第一步。
系统分析员的语言
场景描述是一个很不错的方法,可是随着你对系统的分析的深入,参与开发的人员的增加,你渐渐的感觉这种方法不够用了。原因有很多:文字的描述不够直观,不可能到达一种很细致的程度。这时候就需要一种能够描述问题、描述解决方案、起沟通作用的语言。这就是UML。
UML(Unified Modeling Language 统一建模语言)是由Rational公司发明,目前由OMG(标准化对象管理机构)维护。作为一种建模语言,UML的定义包括UML语义和UML表示法两个部分:
UML语义
描述基于UML的精确元模型定义。元模型为UML的所有元素在语法和语义上提供了简单、一致、通用的定义性说明,使开发者能在语义上取得一致,消除了因人而异的最佳表达方法所造成的影响。此外UML还支持对元模型的扩展定义。
UML表示法
定义UML符号的表示法,为开发者或开发工具使用这些图形符号和文本语法为系统建模提供了标准。这些图形符号和文字所表达的是应用级的模型,在语义上它是UML元模型的实例。标准建模语言UML的重要内容可以由下列五类图(共9种图形)来定义:用例图、静态图、行为图、交互图、实现图。
从应用的角度看,当采用面向对象技术设计系统时,首先是描述需求;其次根据需求建立系统的静态模型,以构造系统的结构;第三步是描述系统的行为。其中在第一步与第二步中所建立的模型都是静态的,包括用例图、类图(包含包)、对象图、组件图和配置图等五个图形,是标准建模语言UML的静态建模机制。其中第三步中所建立的模型或者可以执行,或者表示执行时的时序状态或交互关系。它包括状态图、活动图、顺序图和合作图等四个图形,是标准建模语言UML的动态建模机制。因此,标准建模语言UML的主要内容也可以归纳为静态建模机制和动态建模机制两大类。
这样说,你可能还是不了解UML到底是什么,不过UML并不是我们讨论的重点,你只需要知道UML是一种建模语言,他的目的就是在开发团队之间提供一种通用的、简单的沟通机制,并且UML是面相对象的。
上面所说的这些就是阶段2的重点所在。使用UML语言对阶段1中提出的问题描述进行深化,从各方各面看待问题:顺序、流程、数据、状态、接口等。最终你得到的是一套完整的文档,做为详细的程序设计的参考和标准。
不要累坏自己
上面说了这么多在编程之前要做的工作,但是要注意的是,不是所有的开发工作都必须做这么多的工作的。对于你来说,抓住最重要的内容,不要涉及到太多的细节。对你写出来问题描述,必须要仔细的分析,而这里的分析呢,并不是要你用程序来实现你的问题描述,而是你必须从问题描述中分析出对象(object)、事件(event)和消息(message)。例如上面所提到的例子:
“若采取信用贷款方式,销售员就将顾客信用编号及总金额交给信用部门的信用审核人员。”
你可能看到这句话以后就会在想做一个审核信用的函数,参数部分有信用编号和总金额,然后开始设计程序的细节。如果你是这样干的话,这说明你还没有真正了解OO的思想,你的开发方式仍然停留在以前基于过程的开发方式中。那么面相对象的分析是怎样进行的呢。我们还是来看这段话,撇开你的程序,你的语言,你从这句话中获得了什么信息?是的,你知道说这个企业中有销售员、有信用审核人员;要求审核顾客信用是他们的工作之一;顾客的信用编号和总金额是审核顾客信用的时候的重要信息。这是任何一个普通的人看到这段话后的感觉,这就是OO的分析(在OO中这种的分析方法称为OOAD)。其实是很简单的,和你采用什么样的程序一点关系都没有。
软件重用(Reuse)是软件工程中最重要的思想之一,只有软件重用,才能降低软件成本,提高软件的质量。你在对一个软件进行分析的时候,找出可以重用的对象,有助于你开发高效的软件系统。正如前面所说的,你不必把软件分析的过分细致,你只需从中找出关键性的、能够重用的对象就足够了。剩下的事情,就是对这些对象分配属性和方法,并充分的使用这些对象就好了。
还是那个例子:我们已经从中看到了一些对象:销售员、信用审核人员。但是他们在系统中不具备重用性,至少是重用性不强。信用部门这个对象也存在这个问题。所以不能够把他们作为底层的对象,我们用一个术语Entity Objects 来称呼底层对象。那什么是软件系统中的Entity Objects呢?教导我们要透过现象看本质。上面的描述中有很多隐含的信息:
顾客请求信用贷款:这里就包括两个Entity Objects:顾客、信用贷款。
销售员、信用审核人员都是员工,所以员工也是一个Entity Objects。
观察一个Entity Objects,你会发现,Entity Objects已经是最小的单位,是使用软件的用户(这里是银行)最小的单位。当然不但是这个软件有Entity Objects,比如我们最常用的Word,我们可以想象一下它的Entity Objects有什么,可能是一种叫做图元的Entity Objects,一个图元包括了文字、图象以及其他的对象。
用OO的方法分析你的软件,从设计到实现都是非常的自然,可能你学习面向对象时是从C++开始的,被其中的虚函数、多态性、多基类继承搞得一头雾水。但是实际上OO的方法要比以前的方法简单的多,不相信?试一试,你就知道了。
校订、校订再校订
第3个阶段称为校订,这个称呼真是太贴切了,既不是测试,也不是返工。任何事物从诞生起都必须经历不断的改进才有可能成熟。(呵呵,说出这么有哲理的话,我都很佩服自己啊。)软件同样不例外。事实上,在阶段0中,我们就讨论了“路标”的概念,当你的第一个路标达成之后,剩下的应该都是属于校订的事了。通过和用户的交互,确定新的“路标”,不断的改进系统功能,优化系统结构,修正系统Bug。
正如作者所说的那样,真正OO的软件是经得起修改的,由于采用了多层的结构以及面向对象的思想,以前软件的致命伤(修改)在新的开发方法面前不会是个大问题。(注意,这并不是说你可以无规划构建你的软件)
由于以前的系统大多会将GUI、事务处理、数据存储都做在一起,当你需要修改一个地方的时候,你就会发现问题的严重性:一个小小的需求变动都意味着你的系统将面临伤筋动骨般的修改。在OO的时代里,所有的对象的访问都是通过接口(Interface)进行的,对象中方法的改变并不会接口产生影响,也就是说,调用该接口的模块不用做任何的修改。另外,如果你打算把你的系统的界面从C/S方式向B/S方式改变,同样不会有问题,你可以很容易的把GUI部分从原有的系统中剥离出来。软件的修改对你来说不再是一个恶梦。
大家应该对这两个词很熟悉了,但是对词里包含的意义可能并不是特别清楚。首先必须说明的是,程序员和系统分析员不存在谁高级谁低级的分别,他们是两种职业,对职业技能的要求完全不同。所以厉害的程序员就是系统分析员的说法是不对的。当然,系统分析员的技能要求他必须要懂得如何写程序,但是他的重心在于如何把一个很大的项目切割成适合个人的小块,然后将这些小块组织起来。程序员的职责就是如何更好更快的实现这些小块。
如何设计架构
层层(layer)这个概念在计算机领域是非常了不得的一个概念。计算机本身就体现了一种层的概念:系统调用层、设备驱动层、操作系统层、CPU指令集。每个层都负责自己的职责。网络同样也是层的概念,最著名的OSI的七层协议。
层到了软件领域也一样好用。为什么呢?我们看看使用层技术有什么好处:
●你使用层,但是不需要去了解层的实现细节。
●可以使用另一种技术来改变基础的层,而不会影响上面的层的应用。
●可以减少不同层之间的依赖。
●容易制定出层标准。
●底下的层可以用来建立顶上的层的多项服务。当然,层也有弱点:
●层不可能封装所有的功能,一旦有功能变动,势必要波及所有的层。
●效率降低。
当然,层最难的一个问题还是各个层都有些什么,以及要承担何种责任。
选择一个地方运行领域逻辑
我们的精力集中在逻辑层上。领域逻辑要么运行在Client上,要么运行在Server上。
比较简单的做法是全部集中在Server上。这样你需要使用html的前端以及webserver。这样做的好处是升级和维护都非常的简单,你也不用考虑桌面平台和Server的同步问题,也不用考虑桌面平台的其它软件的兼容问题。
运行在Client适合于要求快速反应和没有联网的情况。在Server端的逻辑,用户的一个再小的请求,也需要信息从Client到Server绕一圈。反应的速度必然慢。再说,网络的覆盖程度也不是说达到了100%。
对于各个层来说,又是怎么样的呢?
基础架构层:一般都是在Server啦,不过有时候也会把数据复制到合适的高性能桌面机,但这是就要考虑同步的问题了。
表示层在何处运行取决于用户界面的设计。一个Windows界面只能在Client运行。而一个Web界面就是在Server运行。也有特别的例子,在桌面机上运行webserver的,例如XServer。但这种情况少的多。
在例1中,没有更多的选择了,只能选在Server端。因此你的每一个bit都会绕一个大圈子。为了提高效率,尽量使用一些纯html脚本。
人们选用Windows界面的原因主要就是需要执行一些非常复杂的任务,需要一个合适的应用程序,而webGUI则无法胜任。这就是例2的做法。不过,人们应该会渐渐适应webGUI,而webGUI的功能也会越来越强大。
剩下的是领域逻辑。你可以全部放在Server,也可以全部放在Client,或是两边都放。
如果是在Client端,你可以考虑全部逻辑都放在Client端,这样至少保证所有的逻辑都在一个地方。而把webserver移至Client,是可以解决没有联网的问题,但对反应时间不会有多大的帮助。你还是可以把逻辑和表示层分离开来。当然,你需要额外的升级和维护的工作。
在Client和Server端都具有逻辑并不是一个好的处理办法。但是对于那些仅有一些领域逻辑的情况是适用的。有一个小窍门,把那些和系统的其它部分没有联系的逻辑封装起来。领域逻辑的接口
你的Server上有一些领域逻辑,要和Client通信,你应该有什么样的接口呢?要么是一个http接口,要么是一个OO接口。
http接口适用于webbrowser,就是说你要选择一个html的表示层。最近的新技术就是webservice,通过基于http、特别是XML进行通信。XML有几个好处:通信量大,结构好,仅需一次的回路。这样远程调用的的开销就小了。同时,XML还是一个标准,支持平台异构。XML又是基于文本的,能够通过防火墙。
虽然XML有那么多的好处,不过一个OO的接口还是有它的价值的。hhtp的接口不明显,不容易看清楚数据是如何处理的。而OO的接口的方法带有变量和名字,容易看出处理的过程。当然,它无法通过防火墙,但可以提供安全和事务之类的控制。
最好的还是取二者所长。OO接口在下,http接口在上。但这样做就会使得实现机制非常的复杂。
组织领域逻辑
要组织基于层的系统,首要的是如何组织领域逻辑。领域逻辑的组织有好几种模式。但其中最重要的莫过于两种方法:TransationScript和DomainModel。选定了其中的一种,其它的都容易决定。不过,这两者之间并没有一条明显的分界线。所以如何选取也是门大学问。一般来说,我们认为领域逻辑比较复杂的系统可以采用DomainModel。
TransationScript就是对表示层用户输入的处理程序。包括验证和计算,存储,调用其它系统的操作,把数据回传给表示层。用户的一个动作表示一个程序,这个程序可以是script,也可以是transation,也可以是几个子程序。在例子1中,检验,在购物车中增加一本书,显示递送状态,都可以是一个TransationScript。
DomainModel是要建立对应领域名词的模型,例如例1中的书、购物车等。检验、计算等处理都放到领域模型中。
TransationScript属于结构性思维,DomainModel属于OO思维。DomainModel比较难使用,一旦习惯,你能够组织更复杂的逻辑,你的思想会更OO。到时候,即使是小的系统,你也会自然的使用DomainModel了。
但如何抉择呢?如果逻辑复杂,那肯定用DomainModel:如果只需要存取数据库,那TransationScript会好一些。但是需求是在不断进化的,你很难保证以后的需求还会如此简单。如果你的团队不善于使用DomainModel,那你需要权衡一下投入产出比。另外,即使是TransationScript,也可以做到把逻辑和基础架构分开,你可以使用Gateway。
对例2,毫无疑问要使用DomainModel。对例1就需要权衡了。而对于例3,你很难说它将来会不会像例2那样,你现在可以使用TransationScript,但未来你可能要使用DomainModel。所以说,架构的决策是至关紧要的。
除了这两种模式,还有其它中庸的模式。UseCaseController就是处于两者之间。只有和单个的用例相关的业务逻辑才放到对象中。所以大致上他们还是在使用TransationScript,而DomainModel只是DatabaseGateway的一组集合而已。我不太用这种模式。
TableModule是另一个中庸模式。很多的GUI环境依托于SQL查询的返回结果。你可以建立内存中的对象,来把GUI和数据库分开来。为每个表写一个模块,因此每一行都需要关键字变量来识别每一个实例。
TableModule适用于很多的组件构建于一个通用关系型数据库之上,而且领域逻辑不太复杂的情况。MicrosoftCOM环境,以及它的带ADO.NET的.NET环境都适合使用这种模式。而对于Java,就不太适用了。
领域逻辑的一个问题是领域对象非常的臃肿。因为对象的行为太多了,类也就太大了。它必须是一个超集。这就要考虑哪些行为是通用的,哪些不是,可以由其它的类来处理,可能是UseCaseController,也可能是表示层。
还有一个问题,复制。他会导致复杂和不一致。这比臃肿的危害更大。所以,宁可臃肿,也不要复制。等到臃肿为害时再处理它吧。
更多的层模式
三层的架构是最为通用的,尤其是对IS系统。其它的架构也有,但是并不适用于任何情况。
第一种是Brownmodel[Brownetal]。它有五个层:表示层(Presentation),控制/中介层(Controller/Mediator),领域层(Domain),数据映射层(DataMapping),和数据源层(DataSource)。它其实就是在三层架构种增加了两个中间层。控制/中介层位于表示层和领域层之间,数据映射层位于领域层和基础架构层之间。
表示层和领域层的中介层,我们通常称之为表示-领域中介层,是一个常用的分层方法,通常针对一些非可视的控件。例如为特定的表示层组织信息格式,在不同的窗口间导航,处理交易边界,提供Server的facade接口(具体实现原理见设计模式)。最大的危险就是,一些领域逻辑被放到这个层里,影响到其它的表示层。
我常常发现把行为分配给表示层是有好处的。这可以简化问题。但表示层模型会比较复杂,所以,把这些行为放到非可视化的对象中,并提取出一个表示-领域中介层还是值得的。
BrownISA
表示层表示层
控制/中介层表示-领域中介层
领域层领域层
数据映射层数据库交互模式中的DatabaseMapper
数据源层基础架构层
领域层和基础架构层之间的中介层属于本书中提到的DatabaseMapper模式,是三种领域层到数据连接的办法之一。和表示-领域中介层一眼,有时候有用,但不是所有时候都有用。
还有一个好的分层架构是J2EE的架构,这方面的讨论可以见『J2EE核心模式』一书。他的分层是客户层(Client),表示层(Presentation),业务层(Business),整合层(Integration),资源层(Resource)。差别如下图:
J2EE核心ISA
客户层运行在客户机上的表示层
表示层运行在服务器上的表示层
业务层领域层
整合层基础架构层
资源层基础架构层通信的外部数据
微软的DNA架构定义了三个层:表示层(presentation),业务层(business),和数据存储层(dataaccess),这和我的架构相似,但是在数据的传递方式上还有很大的不同。在微软的DNA中,各层的操作都基于数据存储层传出的SQL查询结果集。这样的话,实际上是增加了表示层和业务层同数据存储层之间的耦合度。DNA的记录集在层之间的动作类似于DataTransferObject。
典型的三层结构
三层结构估计大家都很熟悉了。就是表示(presentation)层,领域(domain)层,以及基础架构(infrastructure)层。
表示层逻辑主要处理用户和软件的交互。现在最流行的莫过于视窗图形界面(wimp)和基于html的界面了。表示层的主要职责就是为用户提供信息,以及把用户的指令翻译。传送给业务层和基础架构层。基础架构层逻辑包括处理和其他系统的通信,代表系统执行任务。例如数据库系统交互,和其他应用系统的交互等。大多数的信息系统,这个层的最大的逻辑就是存储持久数据。
还有一个就是领域层逻辑,有时也被叫做业务逻辑。它包括输入和存储数据的计算。验证表示层来的数据,根据表示层的指令指派一个基础架构层逻辑。
领域逻辑中,人们总是搞不清楚什么事领域逻辑,什么是其它逻辑。例如,一个销售系统中有这样一个逻辑:如果本月销售量比上个月增长10%,就要用红色标记。要实现这个功能,你可能会把逻辑放在表示层中,比较两个月的数字,如果超出10%,就标记为红色。
这样做,你就把领域逻辑放到了表示层中了。要分离这两个层,你应该现在领域层中提供一个方法,用来比较销售数字的增长。这个方法比较两个月的数字,并返回boolean类型。表示层则简单的调用该方法,如果返回true,则标记为红色。
例子
层技术不存在说永恒的技巧。如何使用都要看具体的情况才能够决定,下面我就列出了三个例子:
例子1:一个电子商务系统。要求能够同时处理大量用户的请求,用户的范围遍及全球,而且数字还在不断增长。但是领域逻辑很简单,无非是订单的处理,以及和库存系统的连接部分。这就要求我们1、表示层要友好,能够适应最广泛的用户,因此采用html技术;2、支持分布式的处理,以胜任同时几千的访问;3、考虑未来的升级。
例子2:一个租借系统。系统的用户少的多,但是领域逻辑很复杂。这就要求我们制作一个领域逻辑非常复杂的系统,另外,还要给他们的用户提供一个方便的输入界面。这样,wimp是一个不错的选择。
例子3:简单的系统。非常简单,用户少、逻辑少。但是也不是没有问题,简单意味着要快速交付,并且还要充分考虑日后的升级。因为需求在不断的增加之中。
何时分层
这样的三个例子,就要求我们不能够一概而论的解决问题,而是应该针对问题的具体情况制定具体的解决方法。这三个例子比较典型。
第二个例子中,可能需要严格的分成三个层次,而且可能还要加上另外的中介(mediating)层。例3则不需要,如果你要做的仅是查看数据,那仅需要几个server页面来放置所有的逻辑就可以了。
我一般会把表示层和领域层/基础架构层分开。除非领域层/基础架构层非常的简单,而我又可以使用工具来轻易的绑定这些层。这种两层架构的最好的例子就是在VB、PB的环境中,很容易就可以构建出一个基于SQL数据库的windows界面的系统。这样的表示层和基础架构层非常的一致,但是一旦验证和计算变得复杂起来,这种方式就存在先天缺陷了。
很多时候,领域层和基础架构层看起来非常类似,这时候,其实是可以把它们放在一起的。可是,当领域层的业务逻辑和基础架构层的组织方式开始不同的时候,你就需要分开二者。
组织webServer
很多使用html方式的人,并不能真正理解这种方式的优点。我们有各种各样好用的工具,但是却搞到让程序难以维护。
在webserver上组织程序的方式大致可以分为两种:脚本和serverpage。
脚本方式就是一个程序,用函数和方法来处理http调用。例如CGI脚本和javaservlet。它和普通的程序并没有什么两样。它从web页面上获得htmlstring形态的数据,有时候还要做一些表达式匹配,这正是perl能够成为CGI脚本的常用语言的原因。而javaservelet则是把这种分析留给程序员,但它允许程序员通过关键字接口来访问信息,这样就会少一些表达式的判断。这种格式的webserver输出是另一种htmlstring,称为response,可以通过流数据来操作。
糟糕的是流数据是非常麻烦的,因此就导致了serverpage的产生,例如PHP,ASP,JSP。
serverpage的方式适合回应(response)的处理比较简单的情况。例如“显示歌曲的明细”,但是你的决策取决于输入的时候,就会比较杂乱。例如“通俗和摇滚的显示格式不同”。
脚步擅长于处理用户交互,serverpage擅长于处理格式化回应信息。所以很自然的就会采用脚本处理请求的交互,使用serverpage处理回应的格式化。这其实就是著名的MVC(ModelViewController)模式中的view/controller的处理。
[img:56646605a7]http://www.csai.com.cn/analyze/howar.gif[/img:56646605a7]
webserver端的MVC工作流程示意图
应用ModelViewController模式首要的一点就是模型要和web服务完全分离开来。使用TransactionScript或DomainModel模式来封装处理流程。
接下来,我们就把剩余的模式归入两类模式中:属于Controller的模式,以及属于View的模式。
View模式
View这边有三种模式:TransformView,TemplateView和TwoStepView。TransformView和TemplateView的处理只有一步,将领域数据转换为html。TwoStepView要经过两步的处理,第一步把领域数据转换为逻辑表示形式,第二步把逻辑表示转换为html。
两步处理的好处是可以将逻辑集中于一处,如果只有一步,变化发生时,你就需要修改每一个屏幕。但这需要你有一个很好的逻辑屏幕结构。如果一个web应用有很多的前端用户时,两步处理就特别的好用。例如航空订票系统。使用不同的第二步处理,就可以获得不同的逻辑屏幕。
使用单步方法有两个可选的模式:TemplateView,TransformView。TemplateView其时就是把代码嵌入到html页面中,就像现在的serverpage技术,如ASP,PHP,JSP。这种模式灵活,强大,但显得杂乱无章。如果你能够把逻辑程序逻辑在页面结构之外进行很好的组织,这种模式还是有它的优点的。
TransformView使用翻译方式。例如XSLT。如果你的领域数据是用XML处理的,那这种模式就特别的好用。
Controller模式
Controller有两种模式。一般我们会根据动作来决定一项控制。动作可能是一个按钮或链接。所这种模式就是ActionController模式。
FrontController更进一步,它把http请求的处理和处理逻辑分离开来。一般是只有一个webhandle来处理所有的请求。你的所有的http请求的处理都由一个对象来负责。你改变动作结构的影响就会降到最小。
View模式
View这边有三种模式:TransformView,TemplateView和TwoStepView。TransformView和TemplateView的处理只有一步,将领域数据转换为html。TwoStepView要经过两步的处理,第一步把领域数据转换为逻辑表示形式,第二步把逻辑表示转换为html。
两步处理的好处是可以将逻辑集中于一处,如果只有一步,变化发生时,你就需要修改每一个屏幕。但这需要你有一个很好的逻辑屏幕结构。如果一个web应用有很多的前端用户时,两步处理就特别的好用。例如航空订票系统。使用不同的第二步处理,就可以获得不同的逻辑屏幕。
使用单步方法有两个可选的模式:TemplateView,TransformView。TemplateView其时就是把代码嵌入到html页面中,就像现在的serverpage技术,如ASP,PHP,JSP。这种模式灵活,强大,但显得杂乱无章。如果你能够把逻辑程序逻辑在页面结构之外进行很好的组织,这种模式还是有它的优点的。
TransformView使用翻译方式。例如XSLT。如果你的领域数据是用XML处理的,那这种模式就特别的好用。
Controller模式
Controller有两种模式。一般我们会根据动作来决定一项控制。动作可能是一个按钮或链接。所这种模式就是ActionController模式。
FrontController更进一步,它把http请求的处理和处理逻辑分离开来。一般是只有一个webhandle来处理所有的请求。你的所有的http请求的处理都由一个对象来负责。你改变动作结构的影响就会降到最小。
本文转自
http://www.csai.com.cn/analyze/howar.htm
里面提到了多种应用的分层方法
在大系统中一般使用分层,良好的分层可以保证系统的设计及维护更方便
在同层中分模块
软件架构设计时容易忽略的几个重要问题
在软件开发中,我们对于软件架构经常看到极端,要么不重视软件架构,要么过分重视以至于她成了“天条”。我甚至遇到了这样的事情,某公司强制推行某基于Struts的架构设计,然而到了项目组它却处处遭到抵制,特别是分部基本上抛弃了这个架构设计。那么,这个原因在哪里呢?为什么一个成本高昂的架构设计没有被接纳呢?实际上有时候一个良好的设计也未必会被接纳,特别是没有Java开发实际经验甚至缺乏软件开发经验的项目经理试图控制技术的时候更加如此。我们抛开这个可能的影响来看待这个问题。
我们发现,很多的设计人员在做软件架构设计的时候忽略了几个重要的问题:
1.软件的架构在大的方向上是固定的
这种固定依据某些基本固定的软件模式(包括设计模式、编码模式或者某些特定的约定)来强化和固定。这使得软件开发在某种程度上具有某些可以明显看得到的风格,这个风格被整个的项目贯穿和坚持,这样当我们进入通常可怕的维护或者调整的时候,我们不会害怕我们在很多种不同的实现中常常迷失了方向。
2.软件的架构在具体的应用中可以适度的可控灵活
毫无疑问,软件设计开发不可能没有例外,在某种程度上保留一些适度的灵活时必要的。一方面,它符合软件开发的实际;一方面,适度的可控灵活体现了一种对实现者的尊重和信任。然而,如何实现可控的灵活呢?通常,我认为可以考虑有限种类的设计选择并通过有色彩的图形来表明这种可选择性。但即便如此,设计者很重要的要考虑这些不同选择之间的关系,以及这些选择和整体设计的兼容性,这个时候,面向接口的设计和适度的“粘合”设计是必要的。
3.架构设计的支撑
架构设计通常会涉及到一些支撑设计,这些设计和实现水准直接的体现了系统的最有价值的部分,更细的划分我们应该可以看到性能和结构相关的部分,以及必要的基础类。通常,作为设计人员,我们应该能够设计并实现系统的性能、结构、扩展、粘合等部分的工作,这部分的工作通常不应该离开设计人员的控制和把握,这些代码的书写或者至少积极的关注是设计人员必要的素质.我们不应该让更多的看到我们无法写出代码来(我也反对没有分工什么都做的设计人员!),实际上,这是软件最困难也是最有乐趣的地方。特别是在测试结果中发现这些设计实现带来的性能的极大提高的时候是非常有成就感的事情。至于基础类,应该是尽可能的减少依赖和耦合的。架构设计的支撑要尽可能的对客户程序员隐藏不必要的中间细节,对基础类要尽量简单、稳定并真正需要。
通常,新兴的技术会用在架构设计的支撑设计里面并被封装以隐藏具体的实现,而通盘应用新兴技术的风险是巨大的,并且对人员的获得也是问题。有些技术是可以提供替代技术的,一些新兴的技术实现也是可以参考的(但未必采用她的实现)。要记住软件开发是需要速度、质量、可维护而不是技术表演,这个度应该由分析设计人员负责任的来把握。
4.面向业务还是面向页面的
也许这种说法会招致一些人的反对,认为自然而然的应该是前者,然而,实际的情况是,我们常常看到打着业务的幌子实现为面向页面的系统,原因很简单.后者更加的“简单”并似乎有更少的代码和工作量。这在某种程度上是对的。然而,区分这两种不同的设计是必要的,尽管你也可能使用了MVC2的架构,但你可以做出面向页面的设计的。观察我们的软件开发,最先提交的代码未必是最少的代价的代码.我们常常可以发现一些项目总是在那些不起眼的地方不断的“修改”,我们的程序员因此好像很忙,但对我们的项目对公司来说这是糟糕的不必要开销。因此,考虑这两种可能的情况,在做出选择之前加以思考是必要的。
5.用团队和别人的眼光考虑问题
实际上,每个项目都是有所差异的,特别是人的差异。很糟糕的是,我们通常不得不面对人力资源的极大压力,特别是这些人之间常常都没有共事的经验也就是常常是临时组成的,而这些人员也通常也缺乏一个软件开发的基本共识.对标准的遵从哪怕是最简单的Java编码规范和对自己提交的产品的简单备注和测试都是缺乏的而通常又没有达到代码就是文档的水准。在这个时候,用团队和别人的眼光考虑问题就是必要的,这实际上就是取技术上的“交集”,也就是走简单的路线。如果没有选择的时候,培训工作就是非常必要的。
用团队和别人的眼光考虑问题也会有其它的问题,特别是当项目压力如进度压力等来临的时候或者项目成员根本不考虑公司整体和长期利益的时候,这个问题很突出,有时候作为分析设计人员你甚至很难解决,特别是你面临刚才我所提到的“没有Java开发实际经验甚至缺乏软件开发经验的项目经理试图控制技术的时候”更加如此。
软件架构的设计说起来简单,也可以说起来复杂,然而,如何把事情做到简单并且把其中的针对性能、分层、粘合、扩展等处理好,并提供可控的灵活性不是容易的。通常,只有多一些经验和教训并能够在软件代码上加以实现核心的才可能成为好的分析设计人员。