UML类图

本文详细介绍了UML类图中的重要概念,包括继承、泛化、实现、依赖、关联、聚合和组成。通过实例解析了它们的区别和使用场景,如Java中的接口继承、类的实现以及不同类型的关联关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

UML类图

统一建模语言(英语:Unified Modeling Language,缩写 UML)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iwCtO8nq-1604042182857)(https://upload.wikimedia.org/wikipedia/commons/9/93/Uml_classes_en.svg)]

PS:上述链接没办法直接被CSDN爬取到,需要查看的话,可以直接访问Uml_classes_en

本文内容大多来自维基百科《类图》或者UML类图官网-The Unified Modeling Language

继承(Inheritance, IS-A)

如果一个类别B“继承自”另一个类别A,就把这个B称为“A的子类”,而把A称为“B的父类别”也可以称“A是B的超类”。用带实线的空心三角箭头表示。继承可以使得子类具有父类别的各种属性和方法,而不需要再次编写相同的代码。在令子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。另外,为子类追加新的属性和方法也是常见的做法。

现今面向对象程序设计技巧中,继承并非以继承类别的“行为”为主,而是继承类别的“类型”,使得组件的类型一致。另外在设计模式中提到一个守则,“多用合成,少用继承”,此守则也是用来处理继承无法在运行期动态扩展行为的遗憾。

比如医院中有病人、医生、护工、护士等等各行各业的人,为了整个模型建立,同时减少重复的模型定义,我们可以定义一个具有标准人(Person)属性的抽象类,然后对于每一个实际人做一个具体的实现,比如下图的病人(Patient)和抽象的人(Person)的继承关系图。

继承

实际例子:

典型的比如Oracle JDK 11java.util中:

  • SetListMapSortedSetSortedMap等接口均继承自Collection接口
  • HashMapLinkedHashMapTreeMap等均继承自AbstractMap
  • ArrayListLinkedListVector等均继承自AbstractList(**注:**这里其实存在一个Java圈比较著名的jdk设计问题,也即Stack为了便捷实现,选择继承Vector而不是最基础的AbstractList,从而造成用户在使用过程中,可以对Stack的任意位置进行直接插入,破坏了Stack的定义,因此官方建议使用Deque模拟栈而不是直接使用Stack类。)

泛化(Generalization)

泛化,即继承的反方向,指的是一个类(称为父类、父接口)具有另外的一个(或一些)类(称为子类、子接口)的共有功能。子类可视为其父类的特例,并可以增加新功能。用带空心三角形箭头的实线表示。

**注:**继承和泛化是同一个关系图(inheritance)的两端。

实现(Realization)

实现(Realization)指的是一个class类实现interface接口(可以是多个)的功能;在Java中此类关系通过关键字implements明确标识。用带空心三角形箭头的虚线表示。

比如一个网站提供给外部一个查询的接口,外部不用关注这个网站怎么实现的查询,只需要知道单击查询(调用查询接口)就可以获得返回结果,所以外部只需要拿到SiteSearch接口即可,而网站自己为了提供服务,需要自己在SearchService中实现对外承诺的功能。

Interface realization dependency from a classifier to an interface.

实际例子

典型的比如Oracle JDK 11java.util中:

  • ArrayListLinkedListVectorAbstractList等均是List接口的实现

举个例子

public interface QueryBuilder {
    String search();
}

public class QueryBuilderImpl implements QueryBuilder {
    @Override
    public String search() {
        return "result of QueryBuilderImpl";
    }
}

public class QueryBuilderAnotherImpl implements QueryBuilder {
    @Override
    public String search() {
        return "result of QueryBuilderAnotherImpl";
    }
}

依赖(Dependency, USE-A)

依赖关系(Dependency)可以简单的理解为一个类A使用到了另一个类B," … uses a …",被依赖的对象只是作为一种工具在使用,而并不持有对它的引用。而这种使用关系是具有偶然性、临时性的、非常弱的,但是B类的变化会影响到A;表现在代码层面,为类B作为参数被类A在某个method(方法)中使用。用带燕尾箭头的虚线表示。表示一个类依赖于另外一个类的定义;依赖关系仅仅描述了类与类之间的一种使用与被使用的关系。

依赖关系通常体现在方法的局部变量、方法入参、方法返回、静态调用中。

比如网购需要使用支付功能:

Web Shopping package uses (depends on) Payment package.

比如汽车工厂能够造出汽车:

UML spec inferior example: Car class has an instantiate dependency on the CarFactory class.

官方定义参考*Dependency in UML*

举个例子

下例中两种SearchServiceFactory的实现和QueryBuilder就存在依赖关系(仅做使用,不做持有)。

public class SearchService {
    QueryBuilder qb;

    SearchService() {
    }

    SearchService(QueryBuilder qb) {
        this.qb = qb;
    }

    public String service() {
        System.out.println("Service begin...");
        String result = qb.search();
        System.out.println("Service end.....");
        return result;
    }
}

/**
 * 局部变量依赖
 */
public class SearchServiceFactory {
    public void getSearchResult(String tag) {
        QueryBuilder qb;
        switch (tag) {
            case "A":
                qb = new QueryBuilderImpl();
                break;
            default:
                qb = new QueryBuilderAnotherImpl();
                break;
        }
        System.out.println(new SearchService(qb).service());
    }
}

/**
 * 注入依赖
 */
public class SearchServiceFactory {
    public void getSearchResult(QueryBuilder qb) {
        System.out.println(new SearchService(qb).service());
    }
}

关联(Association, HAS-A)

关联关系使一个类知道另外一个类的属性和方法;通常含有“知道”、“了解”的含义。通常用于运行时需要动态变化,静态编译不太好决定的关联关系。

**双向(Bi-directional)单向(uni-directional)**的关联是最常见的。

单向关联示意图

比如一个人(SearchService)持有一些衣服(QueryBuilder),***持有方***能够查看***被持有方***所有的公开属性和方法并利用其执行某些特定的动作。

该关联关系通常随着注入方式,在运行时动态决定被持有者QueryBuilder的具体实现。

实际例子

典型的比如Oracle JDK 11TreeMapPriorityQueue等与Comparator的关系。持有方TreeMapPriorityQueue等持有一个比较操作函数接口Comparator,在自己操作过程中执行比较,这个比较方法的具体实现对于持有方而言无需关注,只需调用自身持有的Comparator的操作接口compare即可。

而对于被持有方比较接口Comparator无需关注自己到底被谁持有,只用专注于执行比较操作。

这里需要注意关联依赖的区别。

举个例子

上述SearchService和QueryBuilder,除了依赖关系,还存在关联关系。这里不再重复。

双向关联示意图

一个杂志订阅模型如下:①人持有自己的杂志订阅列表;②杂志也持有自身的订阅客户名单

双向关联示意图

聚合(Aggregate, OWNS-A)

聚合是表示整体与部分的一类特殊的关联关系,是“弱”的包含(" … owns a …" )关系,成分类可以不依靠聚合类而单独存在,可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享(sharable)。例如,池塘与(池塘中的)鸭子。再例如教授课程就是一种聚合关系。又例如图书馆包含(owns a) 学生和书籍。即使没有图书馆,学生亦可以存在,学生和图书馆之间的关系是聚集。聚集可能不涉及两个以上的类。图形以空心的菱形箭尾与实线来表示。

组成(Composition, IS-A-PART-OF)

组成关系,是一类“强”的整体与部分的包含关系(" … is a part of …")。**成分类必须依靠合成类而存在。**整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束。合成类别完全拥有成分类别,负责创建、销毁成分类别。例如汽车与化油器,又例如公司与公司部门就是一种组成关系。图形以实心的菱形箭尾与实线表示。

举个例子

上述SearchService和QueryBuilder就属于关联关系中的组成关系。这里不再重复。

关联、组成和聚合的关联差异

当一个类作为另一个类某个方法的参数或者变量时,为关联。

组成和聚合在代码上编写很类似,两者差别主要在子类的生命周期的不同。**组合关系中,一旦父类被销毁子类也会随之销毁;聚合关系中,子类的生命周期与父类独立。**例如,一个大学包含若干个系(如化学系),每个系由若干名教授。如果大学撤销,这些系也就不复存在了,但这些教授仍会继续存在。还需注意,一名教授可以在不同系甚至不同学校兼职,而一个系不能属于两所大学。所以,大学与系之间是组成关系,而系与教授是聚合关系。

下图为关联、组成和聚合的集成关系图。

UML Association relationship overview diagram.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值