适用于有一定的UML概念及基础某个时间需要重新记忆
🤔 快速回忆
- 车的类图结构为<<abstract>>,表示车是一个抽象类;
- 它有两个继承类:小汽车和自行车;它们之间的关系为实现关系,使用带空心箭头的虚线表示;
- 小汽车为与SUV之间也是继承关系,它们之间的关系为泛化关系,使用带空心箭头的实线表示;
- 小汽车与发动机之间是组合关系,使用带实心箭头的实线表示;
- 学生与班级之间是聚合关系,使用带空心箭头的实线表示;
- 学生与身份证之间为关联关系,使用一根实线表示;
- 学生上学需要用到自行车,与自行车是一种依赖关系,使用带箭头的虚线表示;
👨 类
以
Person
类为例,Java 代码长这样:public class Person { private String ; private int age; public Person(String initialName) { this.name = initialName; this.age = 0; } public void printPerson() { System.out.println(this.name + ", age " + this.age + " years"); } public String getName() { return this.name; } }
对应的类图应该是:
↗️ 关联与依赖
关联 (association)
关联通常表示一个类包含对另一个类的引用,即一个类的实例可以访问另一个类的实例,注意它描述的是对象之间静态的、天然的结构,通常与运行状态无关,可以与依赖关系放一起细细品味。
下面我们有一本书
Book
类,注意到里面有对 Person
对象的引用,那么对应的代码和类图:public class Book { private String name; private String publisher; private Person author; // constructors and methods }
关联关系默认不强调方向,可以是双向的,也可以是单向的,直线表示对象间相互知道,带箭头表明:一本书知道它的作者,但一个人不知道书的存在;在箭头一端添加标签可以进一步描述连接。
那如果一本书有不止一个作者呢?
public class Book { private String name; private String publisher; private List<Person> authors; // constructors and methods }
在类图中,通过在箭头末尾添加星号
*
来描述这种多重性,*
告诉我们一本书可以有多个作者,如果你想更具体,可以标上 1..*
、1..3
表示至少有1个或1~3个。依赖 (dependency)
我们在关联关系中强调了“静态”一词,那么依赖关系则更为“动态”。
依赖关系表示一个类使用另一个类,但这种使用是临时的、短期的。依赖关系表示一个类的方法参数或局部变量引用另一个类,也就是说这种关系通常在运行期间产生,并且随着运行时的变化。
假设我们有一个表示订单
Order
和支付处理 PaymentProcessor
之间的关系,订单类在支付过程中依赖于支付处理类,那么对应的代码和类图:public class Order { private int id; private double amount; public Order(int id, double amount) { this.id = id; this.amount = amount; } public void processPayment(PaymentProcessor processor) { processor.process(amount); } }
public class PaymentProcessor { public void process(double amount) { System.out.println("Processing payment of amount: " + amount); } }
在最终代码中,依赖关系体现为类构造方法及类方法的传入参数,箭头的指向为调用关系;依赖关系除了临时知道对方外,还能“使用”对方的方法和属性;
显然,依赖也有方向,一般都是单向依赖,双向依赖是一种非常糟糕的结构,我们总是应该保持单向依赖,杜绝双向依赖的产生;
⏹️ 聚合与组合
聚合 (Aggregation)
聚合在关联的基础上多了一层语义:表示整体由部分构成。
假设我们有一个表示部门
Department
的类和一个表示员工 Employee
的类。在这个例子中,“部门”包含多个“员工”,但“员工”可以独立于“部门”存在。对应的代码和类图:public class Employee { private String name; private int id; // constructors and methods }
public class Department { private String name; private List<Employee> employees; // constructors and methods }
组合 (composition)
组合是在聚合的基础上更进一层的同生共死的绑定:表示整体由部分构成,但当整体被销毁时,所包含的部分也会被销毁。
假设我们有一个表示房子
House
的类和一个表示房间 Room
的类。在这个例子中,“房子”包含多个“房间”,“房间”不能独立于“房子”存在。对应的代码和类图:public class Room { private String name; private int size; // constructors and methods }
public class House { private String address; private List<Room> rooms; // constructors and methods }
组合和聚合的区别在于:组合关系是一种强依赖的特殊聚合关系,如果整体不存在了,则部分也不存在了,没了房子就没有房间,而在聚合的例子中,没有员工的部门依然可以存在(尽管看起来很诡异)。
🔼 实现与泛化
实现 (realize/implements)
实现关系用于表示一个类实现一个接口。接口定义了一组方法,具体的实现由实现类提供。实现表示的是"can-do"关系,即实现类可以做接口规定的事情。
假设我们有一个接口
Flyable
和一个实现该接口的类 Bird
,表示 Bird
实现了 Flyable
接口的功能。那么对应的代码和类图:public class Bird implements Flyable { public void fly() { System.out.println( "The bird flies."); } }
public interface Flyable { void fly(); }
实现关系表现为继承抽象类或接口;
泛化 (Generalization)
泛化关系用于表示类之间的继承关系,子类继承父类的属性和方法。泛化表示的是"is-a"关系,即子类是父类的一种特殊化。
假设我们有一个基类
Animal
和一个子类 Dog
,表示 Dog
是一种 Animal
。那么对应的代码和类图:
public class Dog extends Animal { public void bark() { System.out.println("The dog barks."); } }
public class Animal { public void eat() { System.out.println("This animal eats food."); } }
泛化关系表现为继承非抽象类;
📚 参考
绘图工具:ioDraw (有点难用😿)