UML软件工程组织

面向对象开发中的本质用例及其职责(3)
作者:Robert Biddle 著,zhangxxin 译 本文选自:UMLChina 2002年10月30日

 

本质用例及其设计


面向对象的设计中,“职责”扮演了非常重要的角色,确切地说,它不仅是CRC技术的关键概念,同样也是职责驱动设计中的关键概念。

在CRC技术中,职责与对象相关联,标识需要解决的问题。在完成职责的过程中,互相协作的对象之间彼此通信,通过不断的优化、迭代,形成一个满意的结构。这样,将职责分配到类,并进行细化,作为一种工具,指导实际系统的开发。

在职责驱动的设计中,职责的内容更广泛更完整。与职责相关联的对象不仅描述了对象维护的知识,还需要描述对象需要完成的动作。在这个意义上,职责强调的是对行为的抽象,而不是具体可能的执行结构的提取。但是,对象可以通过协作对象之间的消息传递完成职责,也可以由此产生更高层次的抽象类。总的来说,在这种设计方法中,职责是在较高层次上进行抽象的分配,而不考虑对象执行的细节。

在CRC技术和职责驱动的设计中,关于职责的概念和使用方法看起来非常类似。它们都认为每一个对象都有一组明确的职责,都包括了进行职责分配,指导设计决策的概念。其实,两者之间本来就有一定的继承关系。面向对象设计的基本原理就是:对象是行为和数据的统一体。职责观点沿袭了这一说法,“职责”本身就包括了职责的内容和可以使用的资源。职责还允许进行授权和管理,通过对一系列子职责的调度和管理完成职责,包括了抽象和封装的概念,正象Wrifs-Brock等人解释的那样:“职责驱动强调对结构和对象行为的封装,关注类所完成的职责,而到实现阶段再考虑类实现的具体细节。”

本质用例的职责描述系统要做什么,而不是做的具体细节。这与对象封装的观点类似,同样也具有类似的优点。

用例中的职责与设计中的职责一样,都描述行为本身,而不是执行的细节,这就可以将需求和设计连结成一个整体来进行描述。

1.确定系统边界

在实际工作中,需求主要是区分系统做什么不做什么。然而,这个边界非常难以确定,也非常难以与涉及的所有人(包括分析员和投资方等)进行沟通,达成共识。我们发现设计中使用的逼近技术非常适用于这种情况。我们把系统当成一个有明显边界的“黑盒子”,用本质用例的系统职责对系统的行为进行描述。

我们把系统看成由一系列职责组成尚未考虑执行细节的独立系统对象。Jacobson等人在不同的方向上提出过类似的观点。采用本质用例、系统职责有助于确定系统边界。如果系统类似一个独立对象,那么用例就采用类似的方法描述,只能从系统的内部访问系统的行为,而交互的过程就类似于一系列能有序管理的参数和返回值。

在UML和RUP中,通用的用例图可以把这些想法固化下来,如图5所示,在图中,我们增加了一个方框,将用户意图和系统职责分离开来,从而明确了系统边界。


银行取款系统的用例表示样例


Jacobson认可这种用法,UML同样认可这种用法,它指出:“用例可以用一个随意封闭的矩形代表系统的边界或分界”。这种用法与系统对象的提法非常一致,便于讨论和确定系统边界。

2.用例的职责和设计

对任何系统来说,本质用例的职责与系统对象内部的职责联系紧密。本质用例的职责反映了整个系统的行为,对象的职责协同工作也应该反映相同的行为。

这种方法将系统的需求和设计联系起来。本质用例的职责可以作为系统设计的起点,指导系统的行为设计,保证设计和用例的可追溯性。

从一系列本质用例开始设计,引出系统对象,通常通过一系列对象的协作完成职责。另外,在设计中,两者可以通过采用相同语言的方法巩固其一致性。

首先,尽可能采用一致的语言,对一系列描述系统对象职责的本质用例进行设计,然后再确定完成相同职责的协作类。

在严格的设计过程中,将根据上面的结果产生具体的类。这个过程是一个重新分配职责的过程,将直接影响最终设计方案的确定,要求非常细致。这个阶段的工作属于设计的基本流程,不可省略。需要一定的设计技巧和技术进行职责再分解。现在有一些成熟的技术可以直接使用,例如,在设计早期就可以使用类析取(Extract Class)技术。

但是,我们不提倡这种做法,主要有三个原因:其一,没用利用任何领域模型,使领域模型和设计脱节,造成理解和维护的困难;其二,一个完整系统有很多用例和职责,难以严格地分解;其三,难以兼顾其它地方产生的设计结构。

在CRC和职责驱动的设计中,基于基本模型和应用领域模型,产生一组关键对象和类,根据领域知识和设计要求进行初步职责分配,然后着眼于少量的关键用例进行交互和迭代,产生初步设计方案。

在CRC和职责驱动的设计中,本质用例没有引起任何流程的改变,但是,它扮演了使用快速原型法进行开发过程中非常有用的角色,为检验设计与需求的一致性提供了有效的方法。

这种方法要从设计初期抓起,从早期用例的职责分配抓起。换句话说,初始的设计职责应该从领域知识中产生。这样,可以与本质用例进行比较,得到早期的反馈,避免以后产生歧义。这样可以更好地指导关键职责设计者的开发工作。

设计一般都会有结果,有许多设计方案可以在新的设计中作为一部分进行继承和重用,例如组件、组件库、框架、设计模式等等,有的甚至本身已经是可执行的,它们对系统设计影响很大。而本质用例则提供最终设计与需求是否一致的有效检验方法。

与CRC和职责驱动的设计不同,其它设计方法与职责本身可以无关,因此,设计和需求的检验显得更加复杂,更加重要,需要对组件和其它结构进行仔细检查。但是,对可执行重用资源的检查并不是对其执行的正确性进行验证,更多的是对其行为模式的认可,也可以说是对其适用性的认可。这样确实对提高重用度具有深远的意义。

用例和设计的职责划分明确以后,问题就迎刃而解了。无论是无意的还是设计活动中的折衷,设计的不完善性都可能存在。无论采用哪种方式,都需要提升设计。另一方面,设计又可以从某种程度上弥补需求的不足。例如,设计必须基于现有系统的处理活动,哪怕仅是需求中的片言只语,这时就可以重新考察用例,看能否有所改变,能否使用现成的设计资源。

综上所述,引入需求和设计职责的概念提高了两者之间的可追溯性,保证需求和设计的一致性。

3.举例说明

在本节中,我们将通过一个小例子来描述我们的工作。为了说明问题,例子非常简单,但是所用的方法同样适用于更复杂的系统,可以用于顶层设计,也可以用于详细设计,并且也有可用的支撑工具。

这个例子是图书馆系统中的一小部分。领域模型由两个对象类组成:书籍和借阅者。分析的过程如图6所示。在顶层,我们只关注于两个本质用例:借书和还书。

通过对这两个本质用例的进一步分析,我们可以得到迭代1中产生的CRC卡片。在这个步骤中,我们不一定能够分析得到所有的系统对象。例如,要知道可借阅的图书,就必须登记已借阅的图书,必须对作为协作对象的职责进行分配。

系统对象应该明确,能够与用例对应,如果用例太多,产生的对象太大,就会难以执行。从领域模型出发,我们建立了三个类:单独的图书馆系统Library System类、书籍Book类(每本书一个实例)和借阅者Borrower类(每个借阅者一个实例),然后考虑它们的职责分配。

迭代2显示了这个设计过程。在设计中,书籍借阅标志的设定在书籍Book类中完成,但是,依然可以通过图书馆系统Library System类进行初始化、查询和更新操作。

图书馆系统Library System类还有一项职责,就是不但需要“知道有哪些图书”,而且还要能够查询。也就是说,图书馆系统Library System类必须对书籍Book类进行管理,同时对借阅者Borrower类也应有相应的管理功能。

在迭代3中,书籍Book类和借阅者Borrower类没有改变,增加了一个独立的分类Catalogue类,完成“知道有哪些图书”的职责,可以用一个标准的collection对象来实现。

需要说明的是,我们在这里基本上只列举在用例中明确描述的职责,实际上,在领域分析的过程中,还会分析产生一些职责,并且对整个过程发生影响。例如,检查书籍是否被借阅的职责就是通过类似领域分析的过程产生的。

如图所示,从需求中提取职责进行设计并不只是一个单向的过程,它可以进行调整和变化。但是,这个过程的描述非常有利于与投资方进行交流,特别是复审阶段的双方沟通。

上一页 下一页



版权所有:UML软件工程组织