7 设计模式原则之合成复用原则
一、什么是合成复用原则?
1.定义:
要尽量使用对象组合(组合关系)来实现代码复用,而不是通过类继承(继承关系)来实现。
2.继承 vs. 组合
- 继承是一种“强耦合”的关系,子类会受父类的影响(例如当父类修改时,子类可能需要跟着修改)。
- 组合则是一种“弱耦合”的关系,通过在类中包含其他对象来实现功能的扩展。
3.合成复用原则的优点
- 降低耦合性:组合关系让类之间的依赖变得更加松散。
- 提高灵活性:组合比继承更容易应对需求变化。
- 增强可维护性:修改或扩展功能时,可以针对组合对象进行调整,而不必修改现有类。
二、为什么需要合成复用原则?
在使用继承的过程中,虽然能快速实现代码复用,但也会带来以下问题:
- 继承违背了封装性:子类直接依赖于父类的实现细节,父类的任何变化都会影响子类。
- 继承缺乏灵活性:继承是一种“静态”的关系,一旦继承关系确定,就无法动态更改父类的功能。
- 可能造成类爆炸:当继承层级过深时,会导致系统变得复杂,难以维护。
通过使用对象组合,可以动态地组合对象的行为,避免继承带来的问题,从而更灵活地实现代码复用。
三、代码举例
1.使用继承实现代码复用
以下代码展示了使用继承实现代码复用的情况:
// 基础车辆类
public class Vehicle {
public void move() {
System.out.println("Vehicle is moving");
}
}
// 汽车类
public class Car extends Vehicle {
public void startEngine() {
System.out.println("Car engine started");
}
}
// 飞机类
public class Airplane extends Vehicle {
public void fly() {
System.out.println("Airplane is flying");
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Car car = new Car();
car.move();
car.startEngine();
Airplane airplane = new Airplane();
airplane.move();
airplane.fly();
}
}
2.问题分析
- 过度依赖继承:
Car
和Airplane
都继承了Vehicle
类。虽然它们可以共享move()
方法,但继承使它们和Vehicle
紧耦合。 - 缺乏灵活性:如果
Vehicle
类中新增了其他方法(比如sail()
),可能会导致Car
和Airplane
类也继承了不需要的功能。 - 扩展困难:当我们需要为某些特殊车辆添加功能时,可能需要再创建更多的子类,造成类爆炸。
3.应用合成复用原则的例子
使用组合重构代码,将车辆的通用功能封装到独立的类中。
// 通用的移动功能
public class Moveable {
public void move() {
System.out.println("Moving...");
}
}
// 通用的飞行功能
public class Flyable {
public void fly() {
System.out.println("Flying...");
}
}
// 通用的引擎功能
public class Engine {
public void start() {
System.out.println("Engine started");
}
}
// 汽车类,使用组合实现功能
public class Car {
private Moveable moveable;
private Engine engine;
public Car() {
this.moveable = new Moveable();
this.engine = new Engine();
}
public void move() {
moveable.move();
}
public void startEngine() {
engine.start();
}
}
// 飞机类,使用组合实现功能
public class Airplane {
private Moveable moveable;
private Flyable flyable;
public Airplane() {
this.moveable = new Moveable();
this.flyable = new Flyable();
}
public void move() {
moveable.move();
}
public void fly() {
flyable.fly();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Car car = new Car();
car.startEngine();
car.move();
Airplane airplane = new Airplane();
airplane.move();
airplane.fly();
}
}
4.改进分析
- 弱耦合:
Car
和Airplane
通过组合的方式使用Moveable
、Flyable
和Engine
对象,它们之间的关系松散,修改任何一个类不会影响其他类。 - 高内聚:每个类职责明确。
Moveable
专注于移动,Flyable
专注于飞行,Engine
专注于引擎启动,符合单一职责原则。 - 灵活性增强:我们可以轻松组合不同的功能。比如,为船只创建一个类时,只需组合
Moveable
和其他功能类即可,无需修改现有类。
四、总结
合成复用原则是一种提高代码复用性和灵活性的重要设计原则。通过优先使用组合而非继承,可以让代码更易于扩展和维护。
原则对比
继承 | 组合 |
---|---|
强耦合,子类依赖于父类 | 弱耦合,类之间通过组合实现 |
不灵活,父类变动会影响子类 | 灵活,可动态更换组合关系 |
适用于“是一个”的关系 | 适用于“有一个”的关系 |
设计建议
- 如果对象之间的关系是“有一个”,优先使用组合。
- 继承仅在表示“是一个”的强关联关系时使用,并避免继承层次过深。
- 使用组合能让代码更符合开闭原则,易于扩展。
通过合理地使用合成复用原则,可以让你的代码更加优雅,具有更高的复用性和灵活性。
五、应用
在实际项目中,识别是否需要使用合成复用原则,可以问自己以下问题:
- 是否需要复用多个功能? 如果是,考虑组合。
- 功能是否会独立变化? 如果是,应该将其分离到独立的类中。
- 是否存在不必要的继承关系? 如果是,重构为组合关系。
赶紧尝试在你的项目中应用合成复用原则吧!让代码更加优雅、高效!