前面提出了软件开发的轮回:期望——破灭——崩溃——新的轮回,我们的解决之道在哪里呢?
我的反思——不在沉默中爆发,就在沉默中灭亡
反思,我在反思……
对于来自客户的变更,我永远忘不了的是大学时老师的谆谆教导。上软件工程课的时候,老师总是一再地反复强调,一定要将需求变更消灭在需求分析阶段。按照过去的瀑布式开发理论的描述,总是要求我们在需求分析阶段了解清楚客户的所有需求,并编写成《软件需求说明书》,交给客户签字。客户一旦在《软件需求说明书》上签字,那么需求就不能再更改了,软件就照这个开发了。但随着时间的推移,软件理论大师们发现,软件设计过程中的业务变更越来越不能为人们所忽视。随后,一个又一个的理论出来了,什么软件设计模式、UML、极限编程(XP)、敏捷开发、迭代开发,等等等等。按照现在的理论,业务变更贯穿到了从需求分析到后期维护的整个软件系统生命周期中,我们随时都在应对着各种不同的业务变更。我们要做的就是通过合理地设计,使我们对业务变更付出的代价达到最小,这也就是我们常说的,设计一个可维护的系统。如果不是应对每一次的业务变更,我们的所有编程理论就变得毫无价值,我们不再需要讲究什么“低耦合、高内聚”,不再需要分层结构,我们将重新回到“天是蓝的,生活是美好的,程序设计也是轻松愉快的”那样一个纯真的年代。但现实就是,我们必须做出改变,我们必须要努力去设计出一个个灵活多变的、可维护的系统。
如何能够保证我们设计出的是可维护的系统呢,大师们通过实践给出了我们一个又一个的方法。总体上说,就是要建立模型,即按照顺序依次建立用例模型、领域模型、分析模型和设计模型,采用迭代的方式一步一步地去设计我们的系统。模型是对我们要解决的问题空间的抽象。我们将现实世界抽象成一个个模型,可以帮助我们更加有序地认识和分析问题,运用我们所掌握的知识,设计出更加合理的系统。
用例模型和领域模型主要是在需求分析阶段完成的,而分析模型和设计模型则主要是在确认需求以后、开始编程以前的分析设计阶段完成的。但它们随着软件开发的整个过程,随着需求的每一次变更也在进行着同步更新,从而形成一个又一个的版本。下面我依次对这四个模型的功能及其各自的作用进行一些简要介绍。
用例模型(Use-Case Model)
用例模型是软件需求分析的开始。当我们的用户不论是用书面的形式还是口头的形式开始向我们表达需求的时候,需求都是凌乱的,需要我们进行整理。我们在整理需求的时候,要将其表达成即能让客户看懂又能让技术人员看懂的图形和文字。这样做,一方面是为了进一步与客户交流,确认需求,另一方面也是为技术人员分析和设计系统提供依据和基础。为了做到这一点,它包含两个部分内容:用例图和用例说明。
用例图关注的是三部分内容:用例、参与者和系统边界。
用例就是一个用来描述参与者如何使用系统来实现其目标的一组场景的集合。听起来有些抽象,我们不妨理解为功能中的一个模块,如在ATM机上操作是一组场景,也就是银行管理系统中的ATM机管理子模块。它可以继续细分成ATM机取款、ATM机查询、ATM机转账等多个场景,也就是ATM机管理子模块下的取款、查询、转账等功能,它们的每一个也就是用例图中的一个个子用例。使用用例分析需求带给我们的重要作用之一,就是将用户所要描述的整个问题空间分割成了一个又一个的模块,然后在每个具体模块下再进一步的描述问题。使用用例的另一个作用就是将客户粗犷的需求描述,转换成了非常严谨的语言表述,详细描述清楚了场景中每一个操作流程的整个过程。同时,该描述中用到的专业词汇的外延与内涵都必须表述清楚,避免出现二义性(关于用例模型的作用、意义、设计过程以及准则,我将在《谈谈用例模型的那些事儿》中详细描述)。
参与者给大家最直观的认识就是操作系统的那些人,但更准确的说应当是操作系统的那些角色。用例模型要求系统分析员描述清楚系统中的每一个场景的每一步操作,操作者是什么角色。然而,参与者除了表示操作系统的人,还表示处于系统边界之外,与系统边界内的用例有关联的其它系统。因此,只有定义好一个系统的边界,才能定义哪些是这个系统内的用例,哪些是这个系统内外的参与者。
系统边界是一个软件系统需要处理的整个问题空间的范围。一个软件系统不可能处理所有问题,我们必须得给它定义这个问题空间的范围。哪些是我们这个软件可以处理的,哪些则是我们这个软件不能处理的,也就是项目管理中所说的项目范围。
用例图可以直观地展现需求中的所有用例、参与者、系统边界,以及它们之间的关系。但是用例图不能够详细描述每个用例、每个参与者、系统边界,以及它们之间关系的定义。同时,每个用例还要描述它的前置条件、后置条件、基本路径、扩展路径等一系列文字。因此,我们需要一个用例说明文档来详细说明用例图。
除了用例图和用例说明,在需要的时候还可以画出一些简单的活动图和状态图,来描述一些复杂的或是关键性的流程。
领域模型(Domain Model)
系统分析员在需求分析阶段,除了建立和编写用例模型以外,还并不足以支持我们后来的系统分析与设计,他还应当建立领域模型。领域模型是在与客户交流的过程中,整个问题空间包含的所有重要概念,及其相互关系的表述。在领域模型中,问题空间又被表述为业务领域。而业务领域中的所有重要概念被描述成了一个又一个的对象或属性。比如,学籍管理是学籍管理系统的业务领域,在这个业务领域中的学生被描述成了“学生”对象,而学生的姓名被描述为“学生”对象中的“姓名” 属性。然而,学生的家庭住址,根据客户的要求,即可以描述为“学生”对象中的“家庭住址”属性,也可以描述为另一个“家庭住址”对象并包含“城市”、“街道”、“通讯地址”和“邮编”等属性。由此可见,领域模型是一堆类图。
领域模型是对业务领域的如实描述,他不包含任何技术实现,也不包含任何分析设计。领域模型关注的是业务领域中的重要概念及其相互之间的关系。领域模型关注的不是业务领域中的所有概念及所有关系。它关注的是对我们要开发的软件系统有用的概念及其关系。领域模型中的概念和关系可能是在与客户交谈的过程中获取(《领域驱动设计》的作者Eric Evans甚至鼓励我们与客户一边交谈一边在纸上画出领域模型),而另一部分是从用例模型的语言描述中获取。
领域模型不是一张大图。一张囊括了业务领域中所有概念和关系的类图常常让人困惑。领域模型应当是一个又一个的场景,我们是在某个场景下描述它的相关概念和关系。(关于领域模型的分析与设计,我将在《谈谈领域模型的那些事儿》中详细描述。)
分析模型(Analyze Model)
当系统分析员与客户进行了一段时间的充分沟通,业务需求基本上确认下来以后,项目开始进入分析设计阶段了。在分析设计阶段的主要工作就是建立分析模型。建立分析模型的工作非常繁杂,它是OOA/D的开始,它需要对需求分析中的每个场景建立静态模型(一系统的类图或对象图,描述系统需要建立的各种类及其相互之间的关系)和动态模型(一系统的行动图、序列图、状态图,描述系统中的所有流程,包括主流程、分支流程,以及流程中各种对象的调用关系,对象在流程中的状态变化)。建立分析模型的工作是一个迭代的过程,起初的迭代是需求的如实描述,接着开始GRASP设计模式以及其它的OO分析理论,对静态模型和动态模型进行优化,寻找更灵活多变、易于维护、高内聚、低耦合的设计方案。
分析模型应当是站在整个系统的高度来看待设计中的每个场景,它必须充分考虑各个模块的协作以及公共代码的复用。因此,那些各个模块都要使用的功能和业务逻辑被提取出来。但是,分析模型与设计模型最大的区别是,它不包含任何实际的技术。你的系统不论采用J2EE来实现,还是.Net来实现,分析模型都是一样的。它也不关注你是采用两层结构还是三层结构,采用的Oracle还是SQL Server数据库,这些都不是它关心的。运用分析模型,我们可以站在一个更高的高度设计我们的系统,使我们的系统规划得更加合理。这也就是我们引入分析模型的重要原因之一吧。(分析模型的建立过程、方法、原则,以及GRASP设计模式,我将在《谈谈分析模型的那些事儿》中详细描述。)
设计模型(Design Model)
分析模型经过数次迭代以后就逐渐过渡到设计模型,并没有一个明确的鸿沟。设计模型的重要区别就是它开始考虑到各种具体的技术。设计模型的产出物就是通过Rational Rose这样的工具正向生成为我们要设计的代码,但这非常理论而不太现实。设计模型最让人困惑的就是它到底细化到什么程度。也就是说,设计模型要设计到多细我们才开始编程。分析设计是系统分析员完成的,而程序设计是程序员完成的。如果设计模型做得太细了,对系统分析员是一个巨大的工作量,而把好多程序员的工作给做了,同时,对程序员的束缚过大。如果当初的设计存在缺陷,将必须返回系统分析员重新修改设计模型,费时费力,得不偿失。所以我认为,设计模型不宜太细,从整体上把握主要部分就可以了。同时,程序员在设计程序的过程中可以自己设计自己的设计模型,这时我们常说的GoF设计模式可以派上用场了。它对我们提高设计水平和代码质量大有帮助。(设计模型的过程、理论,以及一些常用的GoF设计模式,我将在《谈谈设计模型的那些事儿》中详细描述。)
2 楼 joeyon 2011-05-28 22:05
1 楼 zhangnianfu 2011-01-28 15:49