概念:结构化类
结构化类是一种由部件组成的类(通过显式“嵌套”表示),用于对包含分层结构(它们是由部件构成的类)进行建模。
关系
相关元素
主要描述

定义

按照 UML([UML04]),类是 EncapsulatedClassifier 和“元类”类的子类型,使类有能力具有内部结构和端口。此外,UML 将组件定义为类的子类型。因此,在 RUP 环境中,我们将组件均视为构造化类。

部件

构造的类的实例包含对应每个部件的一个或一组对象。当包含的构造类实例被破坏时,所有这样的实例都被破坏。

以下示例显示了 Car 类的两个可能视图:

在图(a)中,显示的 Car 具有角色名 rear 与类 Wheel 的组装关联和角色名 e 与类 Engine 的组装关联。类 Engine 的任何实例可链接到任意数量的类 Wheel 实例。

在图(b)中指定了同样内容。但是,在图(b)中还另外指定了以下内容:

  • reare 属于类 Car 的内部结构。这就支持详细信息的指定只适用于类 Car 环境中的 WheelEngine 类的实例,而不适用于一般的 wheelsengines

  • 在类 Car 的环境内,扮演 e 角色的实例可能只连接到扮演 rear 角色的两个实例。此外,扮演 erear 角色的实例只有在它们是类 Car 的同一个实例的角色时,才可以链接。

  • 换言之,当类 WheelEngine 的实例扮演类 Car 的实例中各自的角色时,会对它们应用额外的约束。这些约束对于一般的 WheelEngine 的实例是不起作用的。其他 wheelengine 可按图(a)中所指定而任意链接。

附带文本中描述的图。

示例:在构造的类中扮演角色的部件

连接器

连接器是构造的类中两个部件之间的关系的实例。它是一个链接,以允许进行通信。连接器可由普通关联或瞬态关系实施,如过程参数、变量、全局值或其他机制。

为装配连接器和委托连接器指定了构造的类的内部“配线”:

  • 在构造的类的实施中,装配连接器连接不同部件的端口。在一个构造的类的端口上发送的消息在另一个构造的类的、连接好的端口上接收。一组部件可通过各自的端口连接起来。一个部件不需要知道关于其他部件的任何信息,只需知道它们存在并符合相连接的端口的约束。 构造的类之间的通信按端口建模。


  • 委托连接器将构造化类的外部端口与其中一个内部部件的端口连接。由外部端口接收的消息传到内部部件的端口;由内部端口发送的消息传到外部端口,然后再传到连接该外部端口的、构造的类。

端口

端口是构造的类的结构特性。通过强制从构造的类的外部到遵守声明接口的端口的通信来增加封装,这为该构造的类的规范和相互连接增加了精确性。

端口需要和提供的接口指定了通过该交互点的交互必需的一切内容。如果通过端口实现构造的类与环境的所有交互,那么构造的类的内部与环境完全隔离。这使得这样一个构造的类可用于符合由端口指定的约束的任何环境。

不存在关于端口如何实施的假定。它可作为显式对象实施,或者只是未在实施中明确出现的虚拟概念。

下面提供了端口示例:

示例 1

附带文本中描述的图。

由 Car 和 Boat 使用的 Engine 端口

上图显示了具有端口 p 和两个接口的类 Engine

  • 提供的接口动力,指定了在此端口上引擎提供的服务(即,通过到达此端口的通信而可进行的操作和接收)。
  • 必需的接口电源,指定了引擎期望环境提供的服务。

在端口 p 上,引擎类是完全封装的;在完全不了解引擎将嵌入的环境的情况下也可以指定它。只要环境遵守引擎的提供和需要的接口所指出的约束,引擎就将正确运行。

为了解释这一点,此示例显示了 Engine 类的两种使用:

  • Car 类通过 axle 连接 engine 端口 p 和一组 wheel。
  • Boat 类通过 shaft 连接 engine 端口 p 和 propeller。

只要引擎和链接到其端口 p 的部件之间的交互遵循约束(由提供的和需要的接口指定),引擎就将按指定运行,无论它是汽车引擎还是船引擎。

而且,即使 Engine 具有其他声明的端口,如用于 Fuel Consumption 的端口 f,汽车的车轮和船的螺旋桨仍将通过端口 p 访问 Engine。端口 f 将是燃料表所感兴趣的,不管使用的燃料类型以及汽车和船可能具有的燃料表类型如何。

示例 2

此端口示例是基于 Java Logging API([JAV03])的,后者是提供 Java 2 平台的核心日志记录工具的以下类和接口的包,其中:

  • 记录器是应用程序作出日志记录调用的主要实体。它用于为特定系统或应用程序组件记录消息
  • 级别提供了日志消息的重要性和紧迫性的指示信息
  • 过滤器提供了记录内容的细分控制,超出了日志级别提供的控制
  • 处理程序从记录器调取消息并将它们导出到不同目标位置(内存、输出流、控制台、文件和套接字)
  • 格式化程序支持日志记录的格式化

那些类和接口在两种重要类型的协作中牵涉到。某些类和接口用于写入日志,而其他类和接口用于管理日志。下图显示了客户和管理员使用日志的两种不同协作,建模成 UML 协作:

  • 协作,其中 LogClient 角色连接到 LogWriter 角色,以便写入日志。
  • 管理协作,其中 LogAdministrator 角色连接到 LogController 角色,以便访问日志和更改日志设置。

附带文本中描述的图。

客户和管理员使用日志的不同协作

对日志记录服务及其协作建模的一种可能的 UML 2.0 表示将使用具有端口和声明接口的组件,如下图所示:

附带文本中描述的图。

作为组件实施的 Java 日志记录 API 包,提供分组到端口中的接口

在 Java 日志记录 API 规范中,一部分日志记录服务被作为类实施,而其他日志记录服务作为接口实现。在此示例中,我们将每个这些服务作为提供的接口建模,这可在组件内部由部件实现。上面提到的与管理协作相关的、两个不同种类的行为可用逻辑上分组到端口中的接口来表示。因此,我们将:

  • LoggerLevel 接口分组到 LogWriter 端口中。由日志客户机访问这些端口,从而写入日志
  • HandlerFilterFormatter 接口分组到 LogController 端口中。由日志管理员访问那些接口,来访问日志和更改日志设置。

此建模备选方案通过在逻辑上将接口分组到不同端口中而分离了问题。我们为组件规范和它与外部世界的相互连接提供了额外的精确性。

建模

在设计期间,类和组件可分解成相连接的部件的集合(可依次进一步分解)。

组合结构图可用于显示构造的类的分解。 作为示例,下图显示了售票系统中票房的组合结构图。此类分解成三个部件:

  • 票销售员接口
  • 按照日期和其他条件检索性能的性能指示信息
  • 包含关于性能和门票的数据的一组数据库。

每个部件通过由各自的端口指定的、定义良好的接口来进行交互。整个票房通过端口与外界交互。此端口上的消息被分派到票销售员类,但票房类的内部结构对于外部客户是不可见的。

附带文本中描述的图。

示例:售票系统的组合结构图。

UML1.x 表示法

请注意,构造的类在 UML 2.0 中是一个新概念。

RUP 定义的很多封装体可使用结构化类来表示(有关该主题的更多信息,请参阅 工作产品:封装体 工作产品指南:封装体)。

如果您的工具只支持 UML 1.5,在 工作产品:封装体 工作产品指南:封装体中还讨论了备选的表示法。

关于更多信息,请参阅UML 1.x 和 UML 2.0 之间的区别