2013年8月12日星期一16:05

通用和用例模型,第1部分

撰写者

介绍

从一个面向对象的示例开始进行准备,本文探讨了用例建模的一般化(和专业化)。第一部分概述了关键概念并讨论了用例参与者的一般化。第二部分介绍了两个附加用例模型元素的概括。

设置阶段

促进对通用性普遍理解的最好方法是通过一个简单的面向对象的示例,尤其是因为用例模型是基于面向对象的原理(i)建立的。该示例中遇到的大多数概念都可以追溯到 [1] [2],这让我印象深刻。

面向对象的例子

想象一下一个用于组织桌椅的面向对象的库存系统。在现实世界中,每张桌子和每把椅子都是一个物理对象。在库存系统的电子世界中,每个实体表都表示为“表”类型的电子对象,每个实体椅子都表示为“椅子”类型的电子对象,因此:

  • 每个对象的类型都表明对象在其整个存在中的状态及其自身(与任何上下文无关)(类型在对象生存期内不会改变)。
  • 对象的类型对应于创建该对象的具体类(ii),因此,在具体类Table中创建了Table类型的对象,而在具体类Chair中创建了Chair类型的对象。

Table和Chair类是预先定义的,并指定在类中创建的对象具有各自特征(例如,特定的属性值和链接)的特征类型(例如,特定的属性和关联)。例如,对于Chair类的hasArmRests属性,Chair对象的值为True或False。

在定义这些具体类时,我们可能会注意到它们具有某些共同的特征(例如,它们都具有“颜色”属性)。这是一条线索,我们可以将这些类概括为一个超类,例如Furniture Piece,以表示桌子和椅子的共同点。

  • 由于属于“家具零件”类的每个对象都是“桌子”或“椅子”,因此,“家具零件”类被称为抽象的,表示无法实例化(不存在“家具零件”类型的对象)。
  • 表示这种情况的另一种方法是将Furniture Piece类“完全分区”(完全细分为子类)。

家具件表示其子类(中的对象)的“一般化”,如下图所示。

VanGalenAug12 01

类层次结构

泛化类会创建一个类层次结构。在此示例中,它从两个具体类的平面结构开始,这些结构反映了它们的对象本身以及它们自身的内容,然后通过添加一个抽象超类将其扩展为层次结构,该抽象超类表示两个子类中的哪些对象具有相同的对象。

上图中的类层次结构在下面表示为类图,并扩展了一些属性(iii),关联和一些操作(请参阅下文)。


VanGalenAug12 02

遗产

类层次结构的一个关键特征是继承,在继承中,为超类定义的各种特征被其子类继承。

继承是一种类构造,而不是实例构造。

  • 例如,这意味着Table和Chair类继承了Furniture Piece类的color属性,因此在创建对象时,必须为每个Table 和 Chair对象分配一个color属性值。
  • 这并不意味着每个Table 和 Chair对象都从相应的Furniture Piece对象继承其特定的颜色(颜色属性值),因为只有Table 和 Chair对象,而没有Furniture Piece对象。

同样,Table 和 Chair类继承了Furniture Piece类中与Location类的关联,因此每个Table 和 Chair对象都可以具有指向“已分配位置”的链接。

专业化

类层次结构也可以由专业化产生。这意味着引入一个子类,并通过定义适用于该子类但不适用于该超类(iv)的各种特性来表示其与父超类的不同之处。

  • 对于超类,子类是超类的“专业化”。
  • 对于两个或多个子类,共享的超类是它们的“泛化”(v)。

在较早的类图中,“表”和“椅子”是“家具零件”的专业化,其中“表”具有maxNumberOfPeople属性(但“家具零件”没有),而“椅子”具有hasArmRests属性和getSeatingPosition操作(但“家具零件”没有)。

以下三个部分介绍了其他专门化示例。

将方法延迟到子类

除了继承属性和关联外,Table 和 Chair还继承了getCapacity操作(vi)。对类的操作表示该类中的对象具有命名行为的能力,在这种情况下,获得其能力。

  • 容量代表相应的物理对象可容纳的人数。
  • 桌子的容纳人数由可坐在桌子旁的最大人数表示,而椅子的容纳人数始终等于一。

家具件的操作以斜体显示,表示它是抽象的。这意味着Furniture Piece不提供该操作的方法,而是将其留给其子类来进行(vii)。相比之下,“桌椅”则以纯字体显示操作,以传达其具体含义。这意味着每个子类为操作提供了自己的方法。

  • 因此,当要求一个Table对象执行该操作时,Table类的方法是返回该表对象的maxNumberOfPeople属性值,而当一个Chair对象被要求执行该操作时,Chair类的操作方法是:返回1。

因此,将为超类的操作提供方法的操作推迟到子类。

覆盖方法

建模getCapacity方法的另一种模式如下。

  • Furniture Piece的getCapacity操作是具体的,其方法是返回1(即默认方法)。
  • Chair继承了Furniture Piece的方法,但是Table定义了自己的方法,该方法将为maxNumberOfPeople属性返回Table对象的值。

因此,Table自己的方法将覆盖Furniture Piece的默认方法。

添加操作及其方法

Chair在继承的椅子上有一项操作。

  • 对于给定的Chair对象,getSeatingPosition操作返回“ in”或“ on”。
  • 相应的方法获取Chair的hasArmRests属性值,并根据此值是True还是False分别返回“ in”或“ on”。

增加的操作有助于将“椅子”定义为“家具件”的特殊化。

Type 和 class

在哲学上,类型(代表某种概念,如椅子)和类(即对象的集合,如桌子和/或椅子)之间有区别。

在类图中,将这两个方面组合在一起,这样,一个类既代表一个概念(通过表征其特征的种类)又代表一个集合(具有针对那些特征的单独特征的对象的集合)。

即使这样,仍然存在语言上的区别,因为对象“是”某种类型,但“属于”一个类(是该类的成员)。至少,某种类型的对象属于创建该对象的具体类。如果该类具有任何超类,则该对象也属于那些超类,尽管是间接的。

类型对应

类型层次结构是衡量类层次结构正确性的关键指标。这意味着语句“<subtype> is a type of <supertype>’ must be true for each combination of subclass 和 superclass (representing the 亚型 和 the 超型, respectively, keeping in mind that a class represents both a type 和 a collection).

  • 例如,“桌子是家具的一种类型”和“椅子是家具的一种类型”。

现在,我们来讨论用例建模中的一般化(和专门化)。 

广义因子

为什么演员可以被概括

[3],Actor是BehavioredClassifier的子类,后者是Classifier的子类。由于泛化是分类器的功能,因此Actor从该类继承该功能。换句话说,演员可以按演员层次排列。

一个例子

一个具体的用例只有一个发起方(viii),但是如果某些系统的用例可以由不同类型的用户(如联络中心专家和客户声明专家)发起,该怎么办? Actor泛化解决了该问题,如下图所示。


VanGalenAug12 03

角色概括(和专业化)创建一个角色层次结构。

演员的意思

较早的面向对象示例处理系统内部的电子对象,每个电子对象都是通过在具体类中创建而形成的。相反,在具体参与者中创建参与者实例的概念不适用,因为:

  • 参与者代表可以使用系统的实际实体(例如,实际的人,实际的系统,实际的功能),这些实体已经独立存在。
  • 演员代表某个实体可能扮演的角色,而不是该实体本身的角色。

当参与者是具体的时,它意味着实体被分配给它,而不是在其中创建实体。

两种模式

实际上,我们需要了解两种建模模式,为此,我使用以下术语和定义。

  • 分类:根据实体所处的位置及其自身来分类实体,以使实体属于一个且仅是一个反映实体类型的具体类,并且在整个存在时都属于该类。
  • 分类:根据时间点规则,判断或偏好将一种或多种类型的实体分配给类别,以使一个实体可以一次属于多个类别,并且可以属于其一部分的类别存在。

因此,较早的类层次结构反映了分类,而以上的参与者层次结构则代表了分类。下图显示了后者(x)。


VanGalenAug12 04

像面向对象示例中的Furniture Piece类一样,Customer Service Specialist actor是抽象的,因为它是完全分区的(即,完全细分为子actor)。

与Furniture Piece类不同,Customer Service Specialist演员不是“ disjoint”,而是“ overlapping”(例如 [3] 之所以如此),是因为一个给定的人原则上可以一次属于多个子角色(这是我们在处理分类而非分类的线索)。

类型对应

对于类层次结构,对于行为者层次结构来说,必须确保类型对应关系,尽管这里的类型严格来说是行为者自己的名字,而不是实际用户(例如Person)的类型(概念性或其他)。

  • 在这种情况下,“联系中心专家是客户服务专员的一种”和“客户声明专家是客户服务专员的一种”是正确的,因此参与者层次是适当的。

角色层次反模式

有时,用例文本主张在两个具体参与者之间添加概括关系。仅当子角色的任何成员和所有成员也始终都是超级角色的成员时,这才是适当的。如果不是,则如下图所示,这种参与者层次结构是一个错误的陈述。


VanGalenAug12 05

以下是参与者层次结构的另外两个示例。


VanGalenAug12 06

为避免不适当的参与者层次结构,请添加一个如上所示的抽象超级参与者,并让具体子参与者的重叠性质处理通过单个分配一个人可以同时属于两个子参与者的场景。

笔记
(i)为简单起见,该示例仅处理一个抽象超类和两个具体子类。

(ii)有些引用实例化对象,这似乎是不合适的。实例化表示通过实例(例如椅子)代表一个概念(例如椅子),因此实例化的是概念,而不是实例。用面向对象的术语,可以实例化具体的类(可以在其中创建对象),而抽象类则不能。

(iii)虽然大多数属性的数据类型均为Boolean,Integer或String,但其余每个属性的数据类型都引用一个枚举,该枚举是该属性允许值的列表。

(iv)专业化不一定与一般化相反。您不能将一个类归类为一个超类,但是可以将一个类归类为一个子类。后者意味着:

  • 该类是具体的开始(对象是或可以在其中创建)。
  • 类的对象的子集将移至子类(即,从类中删除它们,并在子类中使用与子类名称相同的新类型创建对象)。
  • 该类保留其剩余的对象(其类型等于该类的名称)。
  • 换句话说,该类未完全分区。


(v)本文避免使用“通用”和“专业”作为限定词,因为它们是模棱两可的(例如,“通用”是否适用于两个或多个要归纳的类,或代表该概括的类?);而是使用明确的限定词“ super”和“ sub”以及“ generalization”和“ specialization”。

(vi)类别作业:由整个类别执行(例如建立执行个体)。实例操作:由类中的特定对象执行。在本文中,每个操作都是一个实例操作。

(vii)方法:表示当类或对象被要求执行给定操作时将如何响应的表示。

(viii)16.3.6中的例子 [3] “发起一个用例涉及多个参与者实例”可以更简单地表示为一个发起者和一个支持者。

(ix)有些实体在其存在期间会更改类型(例如,将毛毛虫更改为蝴蝶),但是这种类型更改不在本文的讨论范围之内,因为通常不必在典型系统中处理它们。

(x)见16.3.1 [3],“演员可以代表[…]多个不同的实例。”

不要忘记在下面留下您的评论。

参考资料

[1] Meilir Page-Jones,《 UML中的面向对象设计基础》,第四版印刷,2000年9月,Addison-Wesley。

[2] 布鲁斯·韦伯斯特(Bruce F. Webster),《面向对象开发的陷阱:谨慎和热情的指南》,1995年,M&T 图书.

[3] 对象管理组(OMG),OMG统一建模语言TM(OMG UML),上层结构,版本2.4.1。

威廉·范·加伦

威廉·范·加伦(Willem Van Galen)是退休的高级系统分析师,之前曾在加拿大安大略省伦敦的一家大型金融服务公司任职。 从2008年至2016年,他的职责之一是设计,提供和加强用例建模培训,实践和标准。 在2011年,他增强了这些功能,以反映面向服务的架构和事件驱动的架构。

©BA Times.com 2020

麦格雷戈徽标白色网站