第4章餐馆系统:业务建模接下来的四章将考虑一个简单的案例,并给出一个从需求获取到实现的完整开发过程。我们将考虑一次单独的迭代,它通过统一过程标识的主要工作流之中的四个:即需求、分析、设计和实现,用例子说明UML表示法在软件开发中的使用。由于本案例研究的意图在于强调开发的产品而不是过程,所以不会详细考虑由统一过程定义的这些工作流的结构,而在真正需要的地方将在介绍UML表示法的同时,简略介绍开发中涉及的活动。4.1非正式的需求要开发的系统的意图是,通过改进为顾客预定和分配餐台的过程,支持一家餐馆的日常经营。这家餐馆当前采用一个手工预约系统,使用的是保存在一个大文件夹中的手写预约单。图4.1是当前的预约单的一个例子,预约单中的每一行对应餐馆中一张特定的餐台。预约是对特定的一个餐台登记的,每个预约中记录有“餐具”的数目,或者预期进餐者的数目,这样就能够分配一个大小适当的餐台。这家餐馆在晚间供应三次餐点,称为“简餐”、“正餐”和“夜点”时段。但如同预约单所表明的,这些时段无须严格遵守,可以预约跨多个时段的时间。最后,每个预约中要记录联系人的姓名和电话。图4.1手工预约单为了记录各种事情,要在预约单上加一个注文。当一行用餐者到来并在他们的餐台就座时,就划掉相应的预约登记。如果他们就座的不是他们预约的餐台,就画一个箭头从最初预约的餐台指向新的餐台。如果顾客打电话取消预约,并不能从表中真正地擦除,而是做一个预约已经取消的注文。其他的信息,比如到什么时间餐台必须空出来,也可以写在预约单上。如果有空闲的餐台,用餐者当然也可以不提前预约就进餐馆用餐,这被称为“未预约的顾客(walk-in)”,并在预约单中作为预约登记以表示餐台的占用,但不记录顾客的姓名或电话。4.1.1对计算机化系统的需要这家餐馆的管理人员已经确认了很多与手工系统相关的问题。手工系统速度慢,而且,预约登记单很快就变得难以理解。这可能导致经营上的问题,例如,实际上有空餐台而由于这个预约单不是很明显,会妨碍顾客进行预约。没有备份系统:如果一张预约单被毁坏了,餐馆就没有了那个晚上有什么预约的记录。最后,从现有的预约单获取即使很简单的管理数据也很费时,例如餐台的使用率。由于这些以及其他原因,该餐馆意欲开发一个现行的预约单的自动化版本。新系统应该和现有的预约单显示同样的信息,并且有大致相同的格式,使餐馆员工易于转换到新系统。当记录了新的预约时,或者对已有的预约进行修改时,应该立即更新显示,使餐馆员工在工作时总能使用可获得的最新信息。系统必须易于记录餐馆营业时发生的有意义的事情,例如顾客的到来。系统的操作应当尽可能是直接操作屏幕上显示的数据。例如,可以简单地将预约拖动到屏幕上一个适当的位置以改变一个预约的时间或者分配的餐台。4.1.2定义一次迭代迭代和增量的方法建议,一个系统的第一次迭代应该只交付足够使系统提供某些确实有商业价值的核心功能。在餐馆预约系统这个例子中,基本需求是餐馆在营业时记录预约和更新预约单信息的能力。如果这些功能可以使用,则可能用这个系统代替现有的预约单,然后在随后的迭代中再开发其他功能。4.2用例建模在一个系统可能采用的不同视图中,用例视图(usecaseview)被认为是UML中起着支配作用的视图。用例视图描述的是系统外部可见的行为。因此,在软件开发开始于考虑所提出的系统的需求的情况下,用例视图确立了一种强制力量,驱动和约束着后续的开发。用例视图展示的是系统功能的结构化视图。这个视图定义了若干参与者(actors)和这些参与者可以参与的用例(usecase)。这些参与者模型化了用户与系统进行交互时可能充当的角色,一个用例则描述了用户使用系统能够完成的一项特定的任务。用例视图应该包含一组定义了该系统的完整功能的用例,或者至少定义了当前迭代所规定的功能的用例,这些用例应该以在系统支持下能够完成的任务的措词给出。理想地,用例视图应该是客户、最终用户、领域专家、测试人员和任何其他的涉及系统的人员,不需要详细了解系统结构和实现就容易理解的。用例视图不描述软件系统的组织或结构,它的作用是给设计者施加约束,设计者必须设计出一个能够提供用例视图中指定的功能的结构。4.2.1用例一组用例是一个系统的用户能够使用系统完成的不同任务。在这个例子中,我们将简单描述预约系统可能有的一组用例,但是在真正的开发中,用例一般是由分析人员与系统未来的用户进行磋商确定的。餐馆预约系统第一次迭代的意图是允许用户使用一个自动化的预约单。可以通过考虑在系统实现后餐馆员工能够用它来做什么,简单地草拟出这次迭代的一组初步的用例。下面列出了这些用例所支持的主要任务:1.记录一个新的预约信息(“记录预约”)。2.取消一个预约(“取消预约”)。3.记录一位顾客的到来(“记录到达”)。4.将一位顾客从一张餐台移到另一张餐台(“调换餐台”)。一个用例不单是简单地描述了该系统的部分功能,所以有时可以这样描述:一个用例描述了系统能够为一个特定的用户做些什么:一个用例描述的是一个自包含的任务,用户总是愿意把该任务作为他们正常工作的一部分。如果一组用例覆盖了用户使用系统完成的所有功能,这就为系统的功能需求已经完全指定提供了某种保证。上面的列表简单确定和命名了一些候选的用例。为了更完全地理解每个用例中包含什么,必须如4.3节中所说明的那样,写出每个用例的详细描述。4.2.2参与者一个用例描述了系统及其用户之间的一类交互。但是,系统通常有不同种类的用户,他们能够执行系统功能的不同子集。例如,多用户系统通常定义一个角色称为“系统管理员”:这个人有权访问普通用户不能使用的指定类别的功能,例如定义新用户,或者进行系统备份。人与系统在进行交互时能够担任的不同角色称为参与者(actors)。参与者一般对应于对系统的一个特定的访问级别,由参与者能够执行的系统功能的类别定义。在其他情况下,参与者不是如此严格定义的,而是简单对应于一组对系统有不同兴趣的人。在餐馆预约系统的案例中,所提出的用例可以分成两组。第一组由与维护提前预约信息有关的用例组成。顾客将联系餐馆提前预约或取消提前预约,一般地,接待员将接到这些电话并更新预约系统中存储的信息,因此,我们能够确定一个与相应用例关联的参与者。在第二组中有许多任务需要在餐馆营业时执行,包括记录顾客的到来,以及为了适应不可预料的经营需要将一行用餐者从一个餐台移到另一个餐台。这些工作譬如说可能是一个侍者领班的责任,因此我们能够标识另一个与这些用例关联的参与者。区分参与者很重要,参与者是用例模型的一个构成成分,并且是系统的真正用户。一般而言,参与者和用户之间不存在一对一的对应。例如,在一个小餐馆中,同一个人可以作为接待员和领班,可能通过使用具有不同访问特权的密码登录到系统。相反地,对应于一个参与者可能有许多真正的人:大餐馆在餐馆的每个房间或每层都可能有不同的领班。参与者甚至不需要是人类用户,例如,网络中的计算机可以直接互相通信,在某些系统中远程计算机可能最好能作为参与者建模。4.2.3用例图用例图(usecasediagram)以图解的形式概括了系统中的不同参与者和用例,并显示了哪些参与者能够参与哪些用例。餐馆预约系统的初始用例图如图4.2所示,其中包括了上面确定的参与者和用例。图4.2初始用例图用例图最简单的形式只是显示参与者、用例和它们之间的关系。参与者用一个像人一样的图标表示,用例用包含有用例名字的椭圆表示。在参与者参与的或者能够执行一个特定用例的地方,用一个连接参与者和相关用例的关联表示这种关系。UML允许在用例图中包含更多的结构,来定义用例之间以及参与者之间的各种关系。例如,可以定义多个用例共有的一个共享行为,形式化为它在其他用例中的包含。然而,在实践中不值得花费很多时间细化用例图,因为额外的关系对后面的开发起不到很大作用。如下一节所讨论的,更重要得多的是考虑每个用例指定的行为的细节。其他的用例图表示法将在适当的地方介绍。4.3描述用例用例描述了系统和它的用户之间在一定层次上的完整的交互。例如,一个打电话给餐馆进行预约的顾客,会和餐馆的一位将在系统中记录预约的店员讲话。为此,该店员需要充当一个接待员,即使这并不是他们正式职位的描述,并且以某种方式和系统交互。在这种情况下,该店员被认为是接待员参与者的一个实例,发生在接待员和系统之间的交互是用例的一个实例。在用例的不同实例中会发生什么的细节,会在很多方面有所不同。例如接待员必须要为每个新的预约输入特定的数据,如不同顾客的姓名和电话号码,这些数据在各个实例中都不尽相同。更值得注意的是,一个用例实例中可能会出现差错,这样将不能达到原来的目的。例如,在用户要求的时间没有合适的餐台,用例的实例可能实际上将不会导致进行一个新的预约。一个用例的完整描述必须指明,在用例所有可能的实例中可能发生什么。用例描述可能因此而包含大量信息,这就需要某种系统的方法来记录这些信息。但是,UML没有定义一种描述用例的标准方式。这样做的部分原因是,用例的意图是不拘形式地用作与系统未来用户进行沟通的一种辅助工具,所以重要的是开发人员应当有自由,用看来对用户有帮助并且容易理解的各种途径与用户讨论用例。尽管如此,在定义一个用例时能有一些可以考虑的结构还是有用的,为此,许多作者定义了用例描述的模板(template)。一个模板实质上是一个标题列表,每个标题概括了可能记录的一个用例的一些信息。在本章中,将简略讨论用例描述最重要的方面,但在附录C中给出了一个完整的模板。4.3.1事件路径用例描述必须定义在执行用例时用户和系统之间可能的交互。这些交互可以作为一种对话描绘,其中用户对系统执行一些行为,系统于是以某种方式响应。这样的对话一直进行到该用例实例结束。交互可以区分为“正常”交互和其他各种情况的交互。在正常交互中,用例的主要目标可以没有任何问题并且不中断地达到,而在其他情况中一些可选的功能会被调用,或者由于出错以致不能完成正常的交互。正常情况被称为基本事件路径(basiccourseofevents),其他情况称为可选的(alternative)或例外的(exceptional)事件路径,取决于它们被看作是可选的还是错误。一个用例描述的主要部分是对用例所指定的各种事件路径的说明。.例如,在“记录预约”用例中,基本事件路径将描述这样的情况:一位顾客打电话进行预约,在要求的日期和时间有一张合适的餐台是空闲的,接待员输入顾客的姓名和电话号码并记录预约。这样的事件路径,如下所示,能够以稍微结构化的方式表示,以强调用户的动作和系统响应之间的交互:记录预约:基本事件路径1.接待员输入要预约的日期;2.系统显示该日的预约;3.有一张合适的餐台可以使用;接待员输入顾客的姓名和电话号码、预约的时间、用餐人数和餐台号;4.系统记录并显示新的预约。在一个事件路径中,常常会想到包含类似“接待员询问顾客将要来多少人”这样的交互。其实这是背景信息,而不是用例的基本部分。事件路径要记录的重要事情是用户输入到系统的信息,而不是该信息是如何获得的。而且,包含背景的交互会使用例不如它在不包含时该有的可复用性,而且使得系统的描述比本来需要的更复杂。例如,假定在餐馆关门时顾客在答录电话上留下了预约请求,这将由接待员在每天开始营业时处理。上面给出的基本事件路径,对接待员直接同顾客讲话或者从录音信息中取得的详细信息,同样适用:单一的用例“记录预约”将这两种情况都包括在内了。然而,如果用例描述包含对接待员和顾客的对话的引用,在处理一条录音信息时它将不能适用,就需要一个不同的用例。如果在顾客要求的日期和时间没有可用的餐台,上面描述的基本事件路径就不能完成。在这种情况下会发生什么可以通过一个可选事件路径描述,如下所示:记录预约——没有可用的餐台:可选事件路径1.接待员输入要求预约的日期;2.系统显示该日的预约;3.没有合适的餐台可以使用,用例终止。这看起来有些简单,但是至少告诉我们,在这一点必须可能中断基本事件路径。在后续的迭代中,将可能为这种情况定义另外的功能,例如,可能将顾客的请求输入到一个等待名单中。注意,确定是否能够进行预约是接