类图关系:实用分析
分析和改进现有的类图范例
引言
UML等许多建模标准都对其标准采取了通用的方法。在实现和/或维护过程中,这可能会导致组件之间实际关系的模糊,尤其是在类图方面。本文试图采用类图绘制中常见的通用符号,并将其重新定义为更面向实现的模型。至于这种方法是否有益,则由您定夺。
背景
根据我的经验,最常用的建模语言是UML。无论您是否知道这个名字,您可能都见过大致基于UML的类图。让我们回顾一下UML中基本的类关系。
在以下描述中,“A”指基本对象,“B”指“A”指向的对象。
实现和派生都是类定义关系。实现模型的关系类似于类和接口,其中B由A实现。派生或继承模型的关系,其中B被A继承。这两者都很容易理解。
类-实例关系使用依赖、关联、聚合和组合进行建模。依赖表示A使用B,但不持有引用,或者引用是微不足道的(例如,仅用于对象创建)。关联是指持有引用且打算使用该对象。聚合是一种奇特的关系。它模拟了“整体-部分”的“拥有”关系,但由于向关联添加基数可以完成同样的事情,因此它被认为没有明确的实际意义。组合是一种聚合,它还暗示了生命周期控制,有点像“拥有”关系。
问题
标准的类-实例关系在建模引用关系方面做得相当好,但一个问题是引用不是类-实例关系的唯一属性。考虑一下工厂方法模式的以下定义:
Creator
在Operation()
中使用FactoryMethod()
来创建Product
。这就是问题所在。没有脚注、解释或图中的特定命名约定,就无法清楚地看出ConcreteCreator
和ConcreteProduct
之间的依赖关系是一种创建关系。类交互的这个方面没有被建模。像这样一个著名的模式的简单例子,一个快速的脚注就足够了。但是一个包含依赖注入框架的项目呢?该框架管理某些类但不是其他类。那样的话,脚注很快就会变得很混乱。
另一个问题是,标准符号不够精细。如果存在生命周期控制需求但类之间不存在整体-部分关系,您会怎么做?在组合上添加1对1的基数?这感觉就像是给一个更大的问题贴创可贴。
分析
因此,我们需要一种方法来模拟引用关系和创建关系,最好是只使用现有的符号。下表代表了我们想要建模的组合。
Create | 参考 | 生命周期控制 |
---|---|---|
N | N | N |
N | Y | N |
N | Y | Y |
Y | N | N |
Y | Y | N |
Y | Y | Y |
注意:YNY 和 NNY 已被省略,因为没有引用就无法进行生命周期控制。
该表显示,创建是一种独立的二元关系——您创建或不创建。引用和生命周期控制是一种耦合的三元关系——无引用+无LCC,有引用+无LCC,有引用+有LCC。总共有六种不同的关系。
回顾标准符号,箭头已经有了有用的含义——关系是定义关系还是实例关系。这样我们就剩下直线和菱形。直线是二元符号;可以是实线或虚线。菱形是三元符号;它可以不存在、未填充或已填充。
建议解决方案
巧合的是,这意味着创建关系可以通过直线定义,而引用关系可以通过菱形定义。为了尽可能接近符号的传统含义,我提出的关系建模系统是:
在之前的工厂方法示例中,不再需要指定关于创建的脚注——只需使用实线。这里有一个更复杂的抽象工厂模式的例子:
没有脚注,标准图的表达能力要差得多。只指定了引用耦合。提出的图在一开始就表达了多个实现细节。工厂和产品的关系纯粹是创建关系。客户端控制工厂实例的生命周期,并持有对产品的引用,但并不创建它们。这立即使模式显而易见。工厂创建产品;客户端使用它们。
最终想法
在这个新系统中,类关系不再受限于诸如依赖或聚合等通用概念。所有关系都定义明确。此外,由于创建关系和引用关系是独立的,符号是分层的,并且整体关系由这些符号构成。
这些特性使得调整关系更加容易。在前面的抽象工厂示例中,如果由于组合带来的整体-部分和创建假设而需要生命周期控制,从客户端-产品关联转换到客户端-产品组合将会很尴尬。新系统只需将空心菱形更改为实心菱形。在我看来,这使得旨在记录实现的图表更加灵活,更易于维护,也更易于理解。
历史
- 2018年3月1日:首次发布