在软件开发过程中,理解和表达类之间的关系是至关重要的。UML(统一建模语言)是业界广泛使用的建模语言之一,其类图是描述类之间关系的常用工具。然而,UML类图中的标准符号可能不足以清晰地表达所有类型的类实例关系,尤其是在复杂的项目中。本文旨在探讨如何重新定义这些符号,以便更精确地表达类之间的关系,特别是实现和创建关系。
在UML中,类之间的关系通常通过实现(Implementation)、派生(Derivation)、依赖(Dependency)、关联(Association)、聚合(Aggregation)和组合(Composition)等概念来表达。这些关系有助于理解类如何相互作用。然而,这些关系往往只关注引用本身,而忽略了类之间的其他重要属性。
标准的类实例关系虽然适用于建模引用关系,但它们并不能涵盖类实例关系的所有属性。例如,在工厂方法模式中,创建者(Creator)使用工厂方法(FactoryMethod)来创建产品(Product)。如果没有图注、解释或特定的命名约定,很难从类图中看出ConcreteCreator和ConcreteProduct之间的依赖关系是一种创建关系。此外,当项目规模较大,特别是涉及到依赖注入框架时,这种关系可能会变得非常复杂。
需要一种方法来建模引用和创建关系,理想情况下只使用现有的符号。下表展示了希望建模的组合关系:
创建 | 引用 | 生命周期控制 |
---|---|---|
否 | 否 | 否 |
是 | 否 | 否 |
否 | 是 | 否 |
是 | 是 | 否 |
否 | 是 | 是 |
是 | 是 | 是 |
注:NYN和NNY被省略,因为没有引用就不能有生命周期控制。
表格显示创建是一个独立的二元关系——要么创建,要么不创建。引用和生命周期控制是一个耦合的三元关系——没有引用+没有LCC,有引用+没有LCC,有引用+有LCC。它们一起构成了六种不同的关系。
幸运的是,这就意味着创建关系可以通过线条来定义,而引用关系可以通过菱形来定义。尽可能保持符号的传统含义,提出的类关系建模系统如下:
在之前提到的工厂方法示例中,不再需要指定创建的脚注——只需使用实线即可。下面是一个更复杂的抽象工厂模式示例:
// 假设的代码示例
class AbstractFactory {
public Product createProduct();
}
class ConcreteFactory implements AbstractFactory {
public Product createProduct() {
return new ConcreteProduct();
}
}
class Client {
private Factory factory;
private Product product;
public Client(Factory factory) {
this.factory = factory;
}
public void operation() {
this.product = factory.createProduct();
}
}
在标准图中,没有脚注,表达力大大降低。只指定了引用耦合。在提议的图中,立即表达了多个实现细节。工厂和产品之间的关系纯粹是创建关系。客户端控制工厂实例的生命周期,并持有产品的引用,但不创建它们。这使得模式一目了然。工厂创建产品;客户端使用它们。
在这个新系统中,类关系不再受到依赖或聚合等泛化概念的约束。所有关系都定义得很好。此外,由于创建和引用关系是独立的,符号变得分层,整体关系是由这些符号构建的。