类之间的关系
没有对象之间职责协作的设计,就不是正确的面向对象设计。如果我们将对象建模为类,则对象之间的关系就体现为类之间的关系。类之间存在不同的关系,依赖的强弱也各有不同,从强至弱依次为:
继承关系 → 组合关系 → 协作关系
继承关系
继承关系体现了“泛化-特化”的关系,父类提供更加通用的特征,子类在继承了父类的特征之外,提供了符合自身特性的特殊实现。继承关系在 UML 中使用空心三角形加实线的方式来代表子类继承父类,例如矩形类继承自形状类:
继承会导致子类与父类之间形成一种强耦合关系,父类发生任何变更,都会体现到子类中,形成所谓的“脆弱的基(父)类”。在代码实现时,修改父类须得慎之又慎,父类的一处变更可能会影响到它的所有子类,并改变子类的行为。由于继承代表了一种“is”的关系,在领域建模时,父类和子类代表的其实是同一个领域概念的不同层次。
组合关系
组合关系体现了类实例之间整体与部分之间的关系,体现了“has”的概念,即一个类实例“包含了”另一个或多个类实例。组合关系体现了类概念之间的一对一、一对多和多对多关系。依据关系的强弱,组合关系又分别分为“合成(Composition)”关系与“聚合(Aggregation)”关系。
前者的关系更强,例如计算机和 CPU 之间就是合成关系,因为离开了 CPU,计算机就不能正常运行;后者的关系较弱,例如计算机和键盘之间就是聚合关系,即使没有键盘,计算机仍然能够正常运行,还可以使用其他输入设备来取代键盘。
从生命周期的角度看,如果是合成关系,表示这个整体/部分关系属于同一个生命周期,即在创建时,除了要创建代表整体概念的主对象,同时还需要创建代表部分概念的从对象,销毁也当遵循这一依存关系。如果是聚合关系,则可以独立地创建和销毁各自类的对象。
组合关系在 UML 中都用菱形来表示。合成为实心菱形,聚合为空心菱形,以此来形象说明其耦合的强弱。注意,菱形应放在主类一边,例如:
我们还可以在组合关系的连线上通过数字来标记它们之间到底是一对一、一对多还是多对多。例如一个 Computer 可能包含多个 CPU:
协作关系
协作关系造成的耦合最弱,可以理解为是类实例之间的“use”关系。这种协作关系往往通过参数传递给类的实例方法。在 UML 中,往往用一个带箭头的线条来表达究竟是谁依赖谁。若被使用的对象为抽象类型,则线条为虚线,表示协作关系为弱依赖。例如,Driver 类与 Car 类之间的关系:
Car 对象作为 drive() 方法的参数传递给 Driver,由于 Car 是一个抽象类型,因此用虚线箭头来表示。实现代码为:
public abstract class Car {
public abstract void run();
}
public class Driver {
public void drive(Car car) {
car.run();
}
}
