目录
文章目录
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 11
的java.util
中:
Set
、List
、Map
、SortedSet
、SortedMap
等接口均继承自Collection
接口HashMap
、LinkedHashMap
、TreeMap
等均继承自AbstractMap
;ArrayList
、LinkedList
、Vector
等均继承自AbstractList
(**注:**这里其实存在一个Java圈比较著名的jdk设计问题,也即Stack
为了便捷实现,选择继承Vector
而不是最基础的AbstractList
,从而造成用户在使用过程中,可以对Stack
的任意位置进行直接插入,破坏了Stack
的定义,因此官方建议使用Deque
模拟栈而不是直接使用Stack
类。)
泛化(Generalization)
泛化,即继承的反方向,指的是一个类(称为父类、父接口)具有另外的一个(或一些)类(称为子类、子接口)的共有功能。子类可视为其父类的特例,并可以增加新功能。用带空心三角形箭头的实线表示。
**注:**继承和泛化是同一个关系图(inheritance)的两端。
实现(Realization)
实现(Realization)指的是一个class类实现interface接口(可以是多个)的功能;在Java中此类关系通过关键字implements明确标识。用带空心三角形箭头的虚线表示。
比如一个网站提供给外部一个查询的接口,外部不用关注这个网站怎么实现的查询,只需要知道单击查询(调用查询接口)就可以获得返回结果,所以外部只需要拿到SiteSearch接口即可,而网站自己为了提供服务,需要自己在SearchService中实现对外承诺的功能。
实际例子
典型的比如Oracle JDK 11
的java.util
中:
ArrayList
、LinkedList
、Vector
、AbstractList
等均是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(方法)中使用。用带燕尾箭头的虚线表示。表示一个类依赖于另外一个类的定义;依赖关系仅仅描述了类与类之间的一种使用与被使用的关系。
依赖关系通常体现在方法的局部变量、方法入参、方法返回、静态调用中。
比如网购需要使用支付功能:
比如汽车工厂能够造出汽车:
官方定义参考*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 11
中TreeMap
、PriorityQueue
等与Comparator
的关系。持有方TreeMap
、PriorityQueue
等持有一个比较操作函数接口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就属于关联关系中的组成关系。这里不再重复。
关联、组成和聚合的关联差异
当一个类作为另一个类某个方法的参数或者变量时,为关联。
组成和聚合在代码上编写很类似,两者差别主要在子类的生命周期的不同。**组合关系中,一旦父类被销毁子类也会随之销毁;聚合关系中,子类的生命周期与父类独立。**例如,一个大学包含若干个系(如化学系),每个系由若干名教授。如果大学撤销,这些系也就不复存在了,但这些教授仍会继续存在。还需注意,一名教授可以在不同系甚至不同学校兼职,而一个系不能属于两所大学。所以,大学与系之间是组成关系,而系与教授是聚合关系。
下图为关联、组成和聚合的集成关系图。