4 系统设计阶段
4.1 划分子系统
这一过程理论上是属于需求分析阶段,当用例越积越多时,就已经有必要对用例进行整理与分类,将功能相尽或者相关度较高的用例归入一个比较大一点的类里,从而形成组件,再继续归类,从而形成子系统。当然这一步,也可以放到设计阶段来完成。所以将这一部分的内容写到了这里。如果需求阶段已经划分了子系统,在设计阶段直接继承,如果没有,则需在设计阶段的第一环节划分出子系统。对此我不准备再作过多的叙述,各位可参考相关的资料。接口也同样。须各位自行去查考相关的资料,这毕竟只是一个文档,不是一本书。
要说明的一点是,在需求阶段划分出来的子系统,在设计阶段需要再审查一下,进行进一步的优化。优化的过程贯穿于整个设计阶段。接口也同样。这就是跨阶段的迭代,当然要尽量避免这种跨阶段的迭代。如果你有过多的跨阶段迭代,就有必要重新审查一下你的需求分析做的是不是有什么问题了 。
4.2 设计类
类的设计是从分析业务实体(领域模型)开始的。最初的类的设计就是简单的将需求分析阶段分析出来的业务实体用类的形式表现出来。当然业务实体之间的关系,以及业务实体的属性,也都应一并继承下来。做完了这些后我们要做的就是分析类的形为,类的形为也叫类的方法,有些书籍上也叫类的责任或者消息。当然现在我们先不着急做这一步,先将上面分析出来的业务实体 转成类的形式 再讲。类图如下
http://b29.photo.store.qq.com/http_imgload.cgi?/rurl4_b=5bf1a5ff56b4afa0e01a28959277104d18f6938a0c5333efedde4d8779357b554bdacf6e5a494bd350ee956949561ea09b67b64da7d036af10c8e7f9c67da63902793a16d95c0fc864ea4cbd71ad859aaf2fa36f&a=25&b=29
属性信息我就不在这里表述出来了,实在是太烦了,时间也紧。
有了这张图,下面我们开始在他的基础上分析一下类的形为。
分析类的形为的具体方法不在本文档里进行讨论,相关的资料和书籍N多,可以自己去看。
下面仅列出经过简单分析后重新更新过的类图,在这个类图中加入了形为
当然这个类图并不是完善的,也不一定是合理的,在此仅仅只是用来举例而已,各位不用太过深究了。在进行类的行为分析时一定要综合所有的和类有关的用例进行分析。同时记住一个很重要的观点,就是类只对自己负责。用户类绝不会去添加商品,因为添加商品不是用户的责任,而应该是购物车或者订单的责任。购物车类也绝不会去登录。因为登录是用户的责任。而不是购物车的责任。
不知道你是否还记得在进行用例分析时,用例分析完成后,引入了业务场景和用例场景的概念。使之可以对用例的细节进行分析,同时在分析业务场景和用例场景的同时,又进一步的完善了用例。在类中也一样。在把类分析出来后,便开始分析他的形为。然后根据形为开始分析他们之间的交互,在分析交互的过程中进一步的去完善你的类。迭代,又是迭代。是的。在RUP中,迭代是无处不在的。
记住,从需求分析继承过来的类并不是最终的类,需求分析阶段主要面向的还是客户,直接继承过来的类还仅仅停留在业务的层面上,但在进行设计时我们却不得不考虑到业务在计算机里的实现问题,因此会对这些类进行调整,优化,扩展,以使之更适应于计算机的实现。这些调整包括运用面向对象的一些特性,例如继承,重载等对类都行重构。在重构的同时。可能会出现许多与具体业务无关的类,虽说他们和业务没有什么关系。但在使一个业务在计算机中得以实现时却是必不可少的。所以这些类你也必须得如实的反应于你更新后的类图上。当然现在我们还是先不着急去管这些,目前紧要的还是进行类的交互分析。
在类的交互分析中最常用到的是顺序图和状态图。至于交互图爱画不画
一般情况下,一个用例的一个场景必须对应一个顺序图,顺序图是为了表现用例中所涉及的类之间的消息传递的过程,这个表述可能让你有点不明白,换种说法,顺序图就是表现用例中所涉及的类之间的调用顺序。其实第一种表述中的消息指的就是类的方法。前面也已经指出过类的方法的表述有很多种,消息,责任,行为。但是无论怎么表述,内容都还是一样的,只是不同的表述用于不同的语境而已。这个知道即可。
状态图和顺序图有所不同,顺序图的主体是用例,而状态图的主体是类。状态图表现了一个类在其参与的所有用例中的状态的改变及其触发条件。状态图异常复杂,要想真正的理解恐怕还得你去找相关的资料多多的研究一下。这里不再作说明。、
下面我们延续上面的例子,绘制一张订购货物的主场景的顺序图,状态图太复杂了,而且前面也没有列举其它的用例,所以在这里就不画了。
订购货物主成功场景顺序图
在这张图中,并没有画出查找目录的相关交互过程。那是因为本文档在进行业务实体分析时,考虑到只是举个例子而已,故只简单的画了几个实体,思考的并不是很全面。不小心将商品目录这个业务实体给丢掉了。但又为了免于唐突,在这里也就没有把和他相关的交互过程表现出来,要不然突然冒出来一个没见过的类对象。反而更容易让人糊涂。虽然这个业务实体很重要,但在这里我就不因为他而去更新之前的相关图例了。这实在是太费事了。大家知道怎么回事就成了。当然在实际的操作过程中,这种思想可要不得。一旦你发现了这种问题,一定要一直的向上追朔,直到将所有相关的内容和图例全部更新为止。
当然实际情况下,这张序列图对于开发人员来说无疑是一个噩梦,这个序列图所表现的那个用例涉及了过多的间断性动作,导致了这个序列图中很多动作都是不连续的,如果用WEB开发的术语来讲,就是这个序列图中包含了过多的请求:在一个页面只有一个HTTP连接的情况下页面刷新一次叫一个请求,这样在实现时就需要技术人员去把这个序列图分解,然而这个分解过程是很痛苦的,当然这不是开发人员的错,造成这种情况的原因是从一开始(进行用例分析)的时候我们就没有遵照前面提到的一条原则—一个用例最好是表示一个不间断的动作。当然这个问题并不是不可解决的,因为只要将原先的那个庞大的用例进行分解就行了,当然这也并不是说,这条用例就没有存在的必要了,事实上恰恰相反,这种粗粒度的用例却是必不可少的,因为粒度过细,就会导致用例膨胀,如果没有这种粗粒度用例进行归类的话,会导致非常大的混乱,不易让人理解。这就产生矛盾了,一方面你说一个用例最好表示一个不间断的动作,一方面又说大粒度的用例必不可少,^_! 你这不是自己打自己嘴巴吗?事实也确实该打嘴巴,当然打归打问题还得解决,解决的方法其实很简单,只须再引入一些概念即可,即是用例的包含与继承关系,大用例包含多个小用例,当然包含的原始目的是为了用例的重用,但根据目前我的实际经验,把一个大用例完全分解成各个小用例,然后大用例包含所有的分解后的小用例,这些小用例可以继续分解,直到用例是表示一个连续的动作(在WEB中则最有可能的是一次请求,或叫一次会话)为止,也就是说叶子节点上的用例都表示一个连续的动作。绘制序列图时,也只需绘制叶子节点上的用例即可。当然以上是个人经验之总结,对不对我也不太清楚,各位仔细掂量,附 用例与用例之间的包含与扩展的原始意义 作为参考
用例与用例之间的包含与扩展的原始意义:包含关系表示一种从属关系,即子用例是主用例中相对独立的、必须调用的一部分功能。在用例分析中,我们应当将多个用例都共有的、相对独立的功能提取出来形成一个子用例,为日后代码复用提供有力保障。扩展关系表示一个功能是对另一个功能的扩展,即被扩展功能不一定调用扩展功能,但扩展功能是对被扩展功能的加强与延伸。在绘制用例关系时,包含关系应绘制成从主用例指向子用例的虚线箭头,并标注为“include”,表示主用例包含子用例;扩展关系应绘制成从扩展用例指向被扩展用例的虚线箭头,并标注为“extend”,表示扩展用例是对被扩展用例的扩展。虚线箭头在UML中代表的是一种依赖关系,即客户元素了解供应者,并且供应者的变化会影响到客户元素(依赖是从客户元素指向供应者的)。 |
通过对顺序图和状态图的分析,可以让我们更有效的去完善类的属性以及它的方法。从而,使我们的设计更加的接近于客户的要求。但这还远远不够,因为目前我们仅仅是停留在完善的层次上,还不能说是一个优秀的设计。甚至连成功的设计都还算不上。因为到目前为止我们还没有考虑到复用的问题。
复用大致分为三个层次。从低到高依次为,代码级复用,组件级复用,服务级复用。
代码级的复用,主要依靠面向对象的特性,如继承,重载,抽象等。此级别复用的执行者是代码开发人员。(注:建设用组合来代替继承)
组件级的复用,主要依赖于设计模式,例如FACTORY,FAÇADE,ABSTRACT FACTORY等。这一层面的复用的执行者一般为设计人员。
服务级的复用。这个复用的层次最高,理解也最为困难,如有兴趣可自行查看关于SOA以及云计算的相关信息。个人认为这个粒度太粗,把握起来很困难,实际运行时,见机行事。
对于复用的其它相关知识,本文档就不再做什么深入的讨论了,各位也可自行查阅相关的资料,只要记住考虑复用是在已经发现了尽可能完善的类后进行的就可以了。当然这也不一定,如果你的经验够丰富,可能不自觉的在设计前期就已经将复用考虑进去了,所以我们还是具体情况具体对待吧,不用搞的那么死。
4.3 数据库映射
按着上面所述的步骤,经过一次又一次的迭代,一次又一次的反复。终于兴奋的发现,原来我们也可以做出优秀的设计。于是乎我们迫不及待的将这些设计的成果发给了开发人员,并自信的对他们说如果照着这个做,保证能做出来一个优秀的系统。然而事实呢,开发人员拿到我们这些所谓的优秀成果。恐怕除了骂爹骂娘骂你祖宗十八代,其它的什么事也做不了。的确,经过上面的迭代,我们的设计看起来已经非常的优秀了,但事实的情况是:根本无法实现。因为到目前为止我们至少还少考虑了一个极其重要的部分。那就是持久化支持,当然这个名词听起来有点玄乎。说白了,就是怎么保存数据。再白一点就是讲你的这个设计怎么和数据库扯上关系。
当然这里我也只是讲出有这么一个过程。具体怎么映射(扯关系就叫映射),是一个类映射成一张表呢,还是一个类映射成多张表呢,亦或多个类映射成一张表呢。又或者怎么处理有继承关系的类到数据表的映射等等,诸如此类,请自己查阅相关文档。当然处理完成后。别忘了画些ER图啊什么的,这是必须的。
又经过了一个漫长的发现,分析,改正并不断迭代的过程。终于,概要设计的所有东西我们都分析出来了,也完善了。什么子系统,组件,接口,类,以及他们之间的交互,包括数据库都写的很详细很清楚并且很明白了,同时又不辞劳苦的用UML工具,将这些所对应的什么子系统图,组件图,接口图,类图,类顺序图,类状态图,甚至类交互图,包括ER图也都画出来了。然而你却惊奇的发现。当你再次把这些东西发给开发人员,然后告诉他们,你去实现吧的时候,他们仍然骂了你的祖宗十八代。的确,这次你是没有把数据库设计给拉下,但如此混乱的一堆东西,恐怕也是没几个人能够接受的吧。所以下面我有必要象整理分析成果时一样对设计成果也进行一番整理。这并不是件麻烦的事,有现成的格式让你套――概要设计说明书。按着概要设计师的目录,将你之前那一堆毫无章法,东一个西一沱的所谓优秀设计的混乱成果们,分门别类放进去,然后你就可以将这个概念设计说明书放心的传到下一个环节,顺利的结束这段折磨人的旅途了。当然如果下一环节是详细设计说明书的话,恐怕还得逮着你。但一般情况下,详细设计说明书的情况不多,一般都是直接到开发人员哪里去了。
结语:正如开篇所言,推导过程不是一个一成不变的东西,以上的东西仅供参考而已。
开发软件不是生产产品,当然开发软件也不是搞艺术设计,确切的讲,应该是介于这两者之间的东西。鼓励创意的同时又约束部分的形为、需要流程而又不局限于流程。这就是软件设计:一个世界上最差劲的艺术设计,外加一个世界上最糟糕的生产过程