类图
类图主要包含两部分:类定义和类关系
- 类定义:用来表示某个类
- 类关系:用来表示类之间的关系
类定义
类定义图包含三部分:类名称、属性区、方法区
类名称
- abstract类(抽象类)的名字以斜体方式显示
- static字段(静态字段)的名字带有下划线
- abstract方法(抽象方法)的名字以斜体方式显示
- static方法(静态方法)的名字带有下划线
可见性表示
- +号表示public
- -号表示private
#
表示protected- ~表示只有同一包的类才能访问的方法和字段
属性类型表示
- 属性的类型也可以表示出来,也可以不表示
接口
在UML更早的版本中,是通过<<interface>>
标记来表示的。感觉这种表示更加直观,可以看到接口里边的方法
类关系图
在UML中类的关系有如下几种:继承/实现关系、关联关系、依赖关系、组合关系、聚合关系。
继承(Generalization)
继承,通过一条带空心三角形箭头的连接线来表示继承,三角形指向的一段是父类,另一端是子类
实现(Realization)
实现是继承的一种特殊实现,即:当一个类继承某个接口的时候,我们成为实现。通过一条带空心三角箭头的虚线来表示实现,和继承的差别仅在于一个是实线,一个是虚线。
关联(Association)
判断两个类关联关系的时候,其实很多时候是用排除法:两个类有关系,但判断不是其他关系的时候,就可以认为是关联关系。在所有的这些关系中,可以认为关联关系是最弱的一种关系。
一个常见的关联关系例子,你和公司签订了劳动合同,那么合同类、公司类、员工类就有关联关系。在这个样例中,Contract类中甚至都没有出现Company类和Employee类,而是通过companyName来关联Company类,通过employeeName来关联Employee类。
public class Contract{
private String companyName;
private String employeeName;
}
public class Company{
private String name;
}
public class Employee{
private String name;
}
依赖(Dependency)
依赖是比关联更强的一种关系,一个类依赖了另外一个类,就意味着一旦被依赖的类发生改变,则依赖类也必须跟着变化。
Client依赖Supplier。
Supplier翻译为供应商,既然是供应商,总得提供一些什么东西吧,要么供应服务(对应方法调用),要么供应内容(对应数据),甚至供应Supplier本身。
常见的依赖样例:
1.方法调用
这种依赖关系是Client调用了Supplier的方法,也就是使用了Supplier的服务。
以ATM取款机为例,假设有这样一个场景:当人付钱的时候,发现现金不够,则使用信用卡刷卡支付。
public class Person{
private int cash;
private CreditCard card;
public boolean pay(int number){
if(cash<number){
//如下仅是示意,实际业务比这里复杂千万倍
return card.swipe(number);//依赖关系在此
}
cash=cash-number;
return true;
}
}
public class CreditCard{
private int _limit;
public boolean swipe(int number){
if(_limit<number){
return false;
}
//执行刷卡操作
return true;
}
}
2.数据依赖
这种依赖是Client使用了Supplier的属性,也就是使用了Supplier的数据
警察检查身份证时,需要验证此身份证是否是罪犯、平民。
public class Police{
public boolean check(IdentityCard card){ //依赖产生的地方,依赖了IdentityCard的number属性
if(_isCriminal(card.number)){
//将罪犯抓起来
return false;
}
if(_isCitizen(card.number)){ //依赖产生的地方,依赖了IdentityCard的number属性
//放走
return true;
}
}
}
public class IdentityCard{
public String name; //姓名
public int number; //身份证号
public String address; //住址
}
3.对象依赖
对象依赖是指Client需要创造Supplier对象,将其返回给其他类。
需要注意的是,如果Client创造了Supplier对象,但只是调用了Supplier的方法,并没有将Supplier返回给其他类,那么应该是一种“方法调用”依赖,而不是一种对象依赖。
面包机制造面包的例子
public class BreadMaker{
public Bread make(String taste){
return new Bread(taste); //依赖在此,依赖了Bread类本身
}
}
public class Bread{
private String taste;
public Bread(String taste){
taste=taste;
}
}
这是比较常见的UML依赖,也比较容易判断的类型。UML规范本身定义了多大9种依赖,有的依赖定义本身就比较晦涩,更别说在实际中应用,和代码对应起来了。有兴趣的可以详细参考:https://en.wikipedia.org/wiki/Dependency_(UML)
聚合(Aggregation)
相比依赖关系,聚合和组合又是更强的一种关系。
聚合:某个类包含另外一个类,但不负责另外类的维护,且两个类的对象生命周期是不一样的,整体销毁后,部分还能继续存在。
对应到Java代码中,这里提到的类的维护,就是“创建对象实例”。
汽车包含发动机,但发动机并不是汽车自己制造的,而且汽车坏了,还可以将发动机拆出来给其他的汽车来用。所以汽车和发动机是一种聚合关系。
聚合通过一条带空心菱形的连接线来表示,菱形所在的一端是整体,另一端是部分。组合的表达方式和聚合基本一致,只是菱形由空心改为实心的。
public class Car {
private Engine engine;//引擎
private Body body;//车身
private Wheel[] wheels;//车轮
private Break brake;//刹车
//以下的get/set函数,都是因为engine等部分的生命周期并不是Car来管理的
//而是由其他类来管理的,例如:可以使用CarFactory类来制造汽车
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public Body getBody() {
return body;
}
public void setBody(Body body) {
this.body = body;
}
public Wheel[] getWheels() {
return wheels;
}
public void setWheels(Wheel[] wheels) {
this.wheels = wheels;
}
public Break getBrake() {
return brake;
}
public void setBrake(Break brake) {
this.brake = brake;
}
}
public class CarFactory{
public Car makeCar(){
Car car=new Car();
car.setEngine(new Engine());
car.setBody(new Body());
car.setBrake(new Brake());
car.setWheels(new Wheel[4]);
return car;
}
}
组合(Composition)
组合:某个类包含另外一个类,且还要负责另外类的维护,且两个类的对象生命周期是一样的,整体销毁后,部分同样被销毁了。
一棵树包含树叶、树干、树枝、树根,一旦树死了,树叶、树干、树枝、树根就全部死了,也不存在将这些部分放到另外一颗树还能用。所以树和树叶等是一种组合关系。
public class Tree{
private Root root;//树根
private Trunk trunk;//树干
private Leaves leaves;//树枝
Tree(){
//与聚合不同的是,组合关系中的“部分”是由“整体”来负责维护的
//这里表现为在Tree的构造函数中需要初始化Root、Trunk、Leaves
root=new Root();
trunk=new Trunk();
leaves=new Leaves();
}
}