任务:确定设计元素
此任务说明了如何确定子系统、类、接口、事件和信号。
用途
  • 对分析类的交互进行分析,以确定设计模型元素
关系
主要描述

任务:用例分析产生了分析类,分析类表示可以执行行为的概念上的类。在设计中,分析类发展成许多不同种类的设计元素:

  • 类,表示一组在相当程度上细分的职责;
  • 子系统,表示一组粗略划分的职责,可能由一组进一步划分的子系统(但最终是一组类)组成;
  • 活动类,表示系统中的线程;
  • 接口,表示由类或子系统提供的职责的抽象声明。

此外,在设计中,也将确定:

  • 事件,是时间和空间中的相关事件的规范,这些事件通常(如果它们是显著的)需要系统作出某个响应;以及
  • 信号,表示用于与系统内的某些类型的事件通信的异步机制。

这些更细的区分可使我们检查设计的不同方面:

  • 事件和用于与之通信的信号可使我们描述系统必须响应的行为的异步触发器。
  • 类和子系统可使我们将相关职责分组为可相对独立开发的单元;类履行一组基本的相关职责,而子系统又是由类或其他子系统组成的组合构造块。子系统用于将开发团队的工作产品表示为功能的一个不可缺少单元,并且因此用作控制和配置管理单元以及逻辑设计元素。
  • 活动类用于表示系统中的控制线程,允许对并发性建模。活动类通常与其他类(通常但不一定是被动类)组合使用:然后可使用这样一个组合(与协作同样的方式)来对复杂行为建模。

    在实时系统中,使用封装体来代替活动类,提供更强大的语义来简化设计并提高并发应用程序的可靠性。封装体共享类和子系统的某些方面:实际上是封装的类协作,共同表示系统中的控制线程。在以下意义上封装体不同于子系统:封装体是单个设计人员的职责,而子系统(通常)是开发人员团队的职责;不过子系统可包含封装体。 

  • 接口可使我们检查和捕获系统的“缝隙”,用精确的术语定义系统的组成部分将如何互操作。
  • 在实时系统中,将使用协议来精确定义可能在封装体端口上发送和接收的消息。

通过分离问题并单独处理由这些概念表示的每个问题,简化了设计流程并阐明了解决方法。

如果要维护系统模型之间的可跟踪性,则应该在此任务期间进行记录。有关记录设计模型和其他系统模型之间的可跟踪性的更多信息,请参阅指南:设计模型

 UML1.x 表示法

按照 UML 1.5,子系统实际上是只将接口作为公共元素的一种特殊的包。 这些接口提供一层封装,允许子系统的内部设计保持对其他模型元素不可见。概念子系统用于区别于“普通”的包,后者是模型元素的无语义容器;子系统表示具有类似于类的(行为上的)属性的包的特定用途。

在 RUP 中,使用 UML 1.5 表示法表示封装体。该表示法的大部分内容可以在 UML 2.0 中使用概念:结构化类表示。

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

步骤
确定事件和信号
目的 确定系统必须响应的外部和内部的事件和信号。 

事件是引起系统内的某个操作的外部和内部发生的情况。事件及其特征可有助于驱动关键设计元素(如活动类)的确定。

一系列初始的外部事件可从用例模型和参与者与用例的交互中得出。内部事件可从用例流的文本中得出,或在设计发展时确定。

事件的重要特征是:

  • 内部对外部 - 是外部事件还是内部事件?
  • 优先级 - 为了处理该事件,是否需要使其他处理暂挂?
  • 频率 - 该事件发生的频率?
  • 频率分发 - 该事件在固定时间间隔发生,还是存在突发现象?
  • 响应需求 - 系统必须以何速度响应事件(可能需要区分一般情况和最坏情况)。
  • 类型 - 是调用事件、时间事件、信号事件还是更改事件(有关定义请参阅概念:事件和信号)?

事件的特征应按需捕获,以驱动处理事件的设计元素的确定。捕获事件特征在反应(事件驱动)系统中往往是最重要的,但在其他系统中也是有用的,例如使用并发和/或异步消息传递的系统。

异步通信事件可建模成信号,以表达承载的数据或表达信号之间的关系(如泛化关系)。在某些系统中,特别是反应系统中,使从外部设备接收的信号与特定机制(如中断或特定轮询消息)相关是很重要的。

确定类、活动类和子系统
目的 分析类优化成相应的设计模型元素

确定类。 当分析类很简单并已经表示单个逻辑抽象时,可直接映射(1:1)到设计类。通常,实体类在设计中会相对原封不动地保存下来。由于实体类通常也是持久的,因此请确定设计类是否应是持久的,并相应记录在类描述中。

当确定类时,应将类分组到工作产品:设计包中,以用于组织和配置管理目的。 有关如何做出封装决策的更多信息,请参阅工作产品指南:设计包

确定活动类。 请考虑已确定分析对象的环境中的系统并发需求:需要系统响应外部生成的事件吗?并且如果需要,当事件发生时,哪些分析类是“活动”的?用例模型中的外部事件由来自参与者并与用例交互的激励表示。请看相应的用例实现,以查看事件发生时哪些对象进行交互。从将对象一起分组到自主的各组协作对象开始 - 这些分组表示形成组合活动类的组的初始划分。

如果事件具有需要捕获的重要属性,则考虑将它们建模成类,定型为 <<信号>>。在实时系统中,这些确定好的各组对象应分组到封装体中,这些封装体具有强大的封装语义。

活动类的实例表示独立的“逻辑”执行线程。这些“逻辑”执行线程不能与操作系统中的执行线程发生混淆,也不会字面映射到操作系统中的执行线程(虽然在某个时刻会将它们映射到操作系统执行线程)。相反,它们表示解决方法空间中独立的概念上的执行线程。此时在设计中确定它们的目的是为了能够基于系统中的固有“并发缝隙”将解决方法划分成独立单元。以此方法划分工作使得概念上处理并发的问题更加简单,因为独立的执行线程可单独处理,除非它们达到共享底层被动类的程度。

通常,只要在问题域内存在并发和并发冲突,就应该考虑活动类。活动类应该用于表示某个外部并发对象或计算机内的并发活动。这使我们能够监视和控制并发活动。

另一个自然的选择是使用活动类作为连接到计算机的外部物理设备的内部表示,因为那些物理实体在本质上是并发的。这些“设备驱动程序”类不仅用于监视和控制相应的物理设备,而且将系统的剩余部分与设备细节隔离开来。这意味着即使设备使用的技术有发展,系统的剩余部分也不会受影响。

使用活动类的另一个常见情况是表示逻辑并发活动。逻辑活动表示概念上并发的“对象”,例如财务交易或电话呼叫。尽管事实上这些并不直接显示为物理实体(虽然发生在真实世界中),但这样看待它们通常是有原因的。例如,可能需要暂时冻结特定的财务交易以避免并发冲突,或由于系统内的故障而需要放弃此次交易。因为这些概念上的对象需要作为单元来操纵,因此将它们表示为具有各自接口(这些接口提供相应功能)的对象是很方便的。

这种类型的概念对象的特定示例是活动对象控制器。它的目的是持续管理一个或多个其他活动对象。这通常包括将每个对象引入期望的操作状态,在面临各种破坏的情况下(如局部故障)使其维持该状态并使它的操作与其他对象的操作同步。这些活动对象控制器通常是从任务:用例分析期间确定的控制对象发展来的。

由于它们的功能是简单而恰当地解决并发冲突,因此活动类作为共享资源的保护者也是很有用的。在这种情况下,多个并发活动需要的一种或多种资源被封装在一个活动类中。依靠其内置的互斥语义,此类保护者会自动保护这些资源免受并发冲突。

对于实时系统,应使用封装体代替活动类:按照上述启发,即只要确定需要使用活动类,都应替换为封装体。

确定子系统。 当分析类很复杂以至它包含的行为不可能是单独操作的单个类的职责时,应将该分析类映射到设计子系统。使用设计子系统封装这些协作,方法是使子系统客户端可以完全不管子系统的内部设计,即使它们使用该子系统提供的服务也是如此。

子系统建模成 UML 组件,该组件只具有作为公共元素的接口。这些接口提供一层封装,允许子系统的内部设计保持对其他模型元素不可见。概念子系统用于区别于包,这些包是模型元素的无语义容器。

从一组协作分析类中创建子系统的决策很大程度上基于协作是否可以或将要由单独的设计团队独立开发。如果协作可以与协作类一起完全包含在一个包中,则子系统可以提供比简单的包所提供的更强形式的封装。在一个或多个接口后面完全隔离子系统中的内容和协作,以便子系统的客户端仅依赖于该接口。然后子系统的设计者完全与外部依赖关系隔离;需要该设计者(或设计团队)指定如何实现该接口,但他们完全可以自由更改内部子系统设计,而不会影响外部依赖关系。在有大量独立团队的大型系统中,这一程度的分离与正式接口所提供的体系结构实施相结合,是选择子系统而不是简单的包的一个有力的理由。有关影响选择使用子系统作为设计元素的各种因素的更多信息,请参阅工作产品指南:设计子系统

确定子系统接口
目的 确定使系统中的缝隙正式化的设计元素。 

接口定义由某个分类器实现的一组操作。在设计模型中,接口主要用于定义子系统的接口。这不是说接口也不能用于类,而是对于单个类,在该类上定义公共操作(实际上是定义它的“接口”)通常就足够了。接口对于子系统是很重要的,因为它们允许行为声明(接口)与行为实现(子系统内实现接口的特定类)相分离。这种分离为我们提供了增强在系统不同部分上工作的开发团队的独立性的方法,同时保留这些不同部分之间的精确“合同”定义。

为每个子系统确定一组候选接口。使用在前一步中确定的已分组的协作,确定当启动协作时要“激活”的职责。然后,通过确定“客户端”必须提供的信息和协作完成时返回的信息来优化该职责;各组信息成为原型输入和输出参数并返回子系统将实现的操作的值。使用工作产品:特定于项目的指南中定义的命名约定为此操作定义名称。重复此操作,直到子系统将实现的所有操作都已定义。

接下来,按照操作相关的职责,将操作分组在一起。较小的组比较大的组更好,因为组中的操作越少,就越有可能存在一组内聚的公共职责。同样也要关注重用 - 查看相似性,这会使确定相关的可重用功能更容易。但同时,不应花大量时间尝试查找理想的职责分组;请记住,这只是首次划分的分组,优化将在整个精化阶段以迭代方式继续。

查看接口之间的相似性。 从一组候选接口中,查找相似的名称、职责和操作。若相同的操作存在于几个接口中,则重构这几个接口,将共同的操作抽取到一个新的接口中。请确保也查看了现有接口,可能的话重用这些接口。目标是维护接口的内聚性,同时除去接口之间的冗余操作。这将使接口更易理解并更易随着时间发展下去。

定义接口依赖关系。 每个接口操作的参数和返回值都具有特定类型:它们必须实现特定接口或必须是简单数据类型的实例。在参数是实现特定接口的对象的情况下,定义接口及其所依赖的接口之间的依赖关系。定义接口之间的依赖关系为软件设计人员提供了有用的耦合信息,因为接口依赖关系定义了设计模型中的元素之间的主要依赖关系。

将接口映射到子系统。 一旦确定了接口,就创建子系统和它实现的接口之间的实现关联。从子系统到接口的实现表明子系统中存在实现接口操作的一个或多个元素。当以后设计子系统时,将优化这些子系统-接口实现,通过子系统设计人员指定子系统内实现接口操作的特定元素。这些优化的实现只对子系统设计人员可见;从子系统客户端的角度来看,只能看到子系统-接口实现。

定义接口指定的行为。 接口通常为实现接口的元素定义隐式状态机。如果接口上的操作必须按特定顺序调用(如数据库连接必须先打开再使用),应该定义状态机,该状态机阐明公开可视的(或推测的)状态(这些状态是实现接口的任何设计元素必须支持的)。 此状态机将帮助接口用户更好了解接口,并将帮助元素(用于实现该接口)的设计人员提供元素的正确行为。

封装接口。 接口由软件设计人员所有;对接口的更改在体系结构上始终是很重要的。要管理对接口的更改,应将接口分组到由软件设计人员所有的一个或多个包中。如果所有接口是由一个子系统实现的,则可将接口与子系统放在同一个包中。如果接口是由多个子系统实现的,则应放在由软件设计人员所有的单独的包中。这样可在独立于各自的子系统的情况下管理和控制接口。

确定封装体协议

目的

确定使系统中的缝隙正式化的设计元素(仅 RT 设计)。

在事件驱动的系统中协议类似于接口:它们通过定义用于独立控制线程之间通信的一组匹配的信号来确定封装体之间的“合同”。接口主要用于使用函数调用模型来定义同步消息传递,而协议主要用于使用基于信号的消息传递来定义异步通信。协议允许行为声明(一组信号)与行为实现(子系统内实现接口的元素)相分离。这种分离为我们提供了增强在系统不同部分上工作的开发团队的独立性的方法,同时保留这些不同部分之间的精确“合同”定义。

为每个封装体确定一组进出信号。使用在前几步中确定的已分组的协作,确定当启动协作时要“激活”的职责。然后,通过确定“客户端”必须提供的信息和协作完成时返回的信息来优化该职责;各组信息成为信号的原型输入参数,该信号将由封装体通过它的一个端口实现。使用工作产品:特定于项目的指南中定义的命名约定为此信号定义名称。重复此操作,直到封装体将实现的所有信号都已定义。

接下来,按照信号相关的职责,将信号分组在一起。较小的组比较大的组更好,因为组中的信号越少,就越有可能存在一组内聚的公共职责。同样也要关注重用 - 查看相似性,这会使确定相关的可重用功能更容易。但同时,不应花大量时间尝试查找理想的职责分组;请记住,这只是首次划分的分组,优化将在整个精化阶段以迭代方式继续。为协议指定一个有意义的描述协议在封装体协作中扮演的角色的名称。

查看协议之间的相似性。 从一组候选协议中,查找相似的名称、职责和信号。若相同的信号存在于几个协议中,则重构这几个协议,将共同的信号抽取到一个新的接口中。请确保也查看了现有协议,可能的话重用这些协议。目的是维护协议的内聚性,同时除去协议之间的冗余信号。 这将使协议更易理解并更易随着时间发展下去。

将协议映射到封装体。 一旦确定了协议,就在实现协议的封装体上创建端口。封装体的端口定义封装体的“接口”,即可由封装体请求的行为。当以后设计封装体时,由端口指定的行为将用封装体的状态机描述。

定义协议指定的行为。协议通常为实现接口的元素定义隐式状态机。如果必须按特定顺序接收接口上的输入信号(如必须先接收“系统就绪”信号再接收特定错误信号),则应该定义状态机,该状态机阐明公开可视的(或推测的)状态(这些状态是实现协议的任何设计元素必须支持的)。 此状态机将帮助封装体(用于实现协议)的用户更好了解封装体的行为,并将帮助封装体的设计人员提供元素的正确行为。

封装协议。 协议由软件设计人员所有;对协议的更改在体系结构上始终是很重要的。要管理对协议的更改,应将接口分组到由软件设计人员所有的一个或多个包中。这样可在独立于实现协议的封装体的情况下管理和控制协议。


属性
多次出现
事件驱动
正在进行
可选
已计划
可重复
更多信息