桥接模式:优雅地分离抽象和实现
在软件系统设计和开发中,我们经常需要处理抽象部分和实现部分之间的关系。这些部分具有不同的变化频率,且可能会在不同的时间发生变化,因此我们需要一种方法来将它们分离并隔离变化,从而避免对整个系统造成影响。
什么是桥接模式
"将抽象部分与实现部分分离,使它们可以独立变化"。——《设计模式:可复用面向对象软件的基础》
这个定义简明扼要地概括了桥接模式的核心思想:将抽象部分和实现部分分离,使它们可以独立变化。这种分离提高了代码的灵活性和可扩展性,并且可以在运行时动态地选择具体的实现类,并将其绑定到抽象部分。
桥接模式的基本原理
桥接模式的基本原理是通过分离抽象和实现,将它们各自封装在不同的类层次结构中,从而使它们可以独立地变化。桥接模式可以用于处理抽象和实现之间的耦合关系,从而提高代码的灵活性和可扩展性。
组成成分
-
抽象化(Abstraction):它是一个抽象类或接口,定义了代表抽象概念的基本操作。该抽象类或接口将实现部分委托给实现化对象(Implementor)。通常情况下,抽象化角色的方法会调用实现化角色的相应方法,起到桥接的作用。
-
扩展抽象化(Refined Abstraction):继承或实现抽象化类的具体子类。这些子类可以对抽象化类进行扩展,增加新的方法和功能。同时,它们还通过实现化对象(Implementor)来调用具体的实现方法。
-
实现化(Implementor):一个接口或抽象类,定义了实现化部分的通用接口,即具体实现的基本操作。它规定了与抽象化角色相对应的具体实现方法,但不具体实现这些方法。
-
具体实现化(Concrete Implementor):实现化接口或抽象类的具体实现类。这些类实现了实现化接口所定义的方法,提供具体的实现逻辑。
桥接模式代码实现
我们以一个图形绘制系统为例来说明桥接模式。假设我们有多种不同类型的图形(如圆形、矩形)和多种不同的绘制方法(如绘制到屏幕、绘制到打印机)。我们可以使用桥接模式来将图形类型和绘制方法分离,使得它们可以独立变化。
1. 实现化(Implementor):定义 DrawAPI 接口,规定了绘制方法的通用接口。
// 定义一个绘制接口
public interface DrawAPI {
// 绘制形状方法,参数为形状类型
void drawShape(String shapeType);
}
2. 具体实现化(Concrete Implementor):创建 DrawAPI 接口的具体实现类 ScreenDrawAPI 和 PrinterDrawAPI。
// 创建 DrawAPI 接口的具体实现类,用于在屏幕上绘制图形
public class ScreenDrawAPI implements DrawAPI {
// 实现绘制形状方法
@Override
public void drawShape(String shapeType) {
System.out.println("在屏幕上绘制 " + shapeType);
}
}
// 创建 DrawAPI 接口的具体实现类,用于在打印机上绘制图形
public class PrinterDrawAPI implements DrawAPI {
// 实现绘制形状方法
@Override
public void drawShape(String shapeType) {
System.out.println("在打印机上绘制 " + shapeType);
}
}
3. 抽象化(Abstraction):定义一个 Shape 抽象类,包含一个绘制方法 draw()。Shape 类将实现部分委托给 DrawAPI 接口。
// 定义一个抽象的形状类
public abstract class Shape {
// 保护类型的 DrawAPI 变量,用于调用具体的绘制方法
protected DrawAPI drawAPI;
// 构造函数,接收一个 DrawAPI 类型的参数
public Shape(DrawAPI drawAPI) {
this.drawAPI = drawAPI;
}
// 抽象的绘制方法,由子类实现
public abstract void draw();
}
4. 扩展抽象化(Refined Abstraction):创建 Shape 抽象类的具体子类 Circle 和 Rectangle。
// 创建一个圆形类,继承 Shape 抽象类
public class Circle extends Shape {
// 构造函数,接收一个 DrawAPI 类型的参数
public Circle(DrawAPI drawAPI) {
super(drawAPI);
}
// 实现绘制方法
@Override
public void draw() {
drawAPI.drawShape("圆形");
}
}
// 创建一个矩形类,继承 Shape 抽象类
public class Rectangle extends Shape {
// 构造函数,接收一个 DrawAPI 类型的参数
public Rectangle(DrawAPI drawAPI) {
super(drawAPI);
}
// 实现绘制方法
@Override
public void draw() {
drawAPI.drawShape("矩形");
}
}
5. 客户端代码:创建具体的图形实例并调用绘制方法。
public class BridgePatternDemo {
public static void main(String[] args) {
// 创建一个屏幕绘制实例
DrawAPI screenDrawAPI = new ScreenDrawAPI();
// 创建一个打印机绘制实例
DrawAPI printerDrawAPI = new PrinterDrawAPI();
// 使用屏幕绘制实例创建一个圆形
Shape circle = new Circle(screenDrawAPI);
// 在屏幕上绘制圆形
circle.draw();
// 使用打印机绘制实例创建一个圆形
Shape circle2 = new Circle(printerDrawAPI);
// 在打印机上绘制圆形
circle2.draw();
}
}
经典类图
Abstraction和Implementor之间架了一座桥(聚合线)
总结
桥接模式作为一种结构型设计模式,它主要目的是将抽象部分与实现部分分离,使它们可以独立变化。以下是桥接模式的优缺点:
优点:
-
分离抽象和实现:桥接模式分离了抽象部分和实现部分,使得它们可以独立地进行扩展,而不是紧密耦合在一起。这有助于提高系统的可扩展性。
-
提高了系统的可扩展性:由于桥接模式允许抽象部分和实现部分独立变化,因此可以在不影响现有代码的基础上,添加新的抽象类或实现类。
-
符合开闭原则:桥接模式提供了一种可扩展的设计,使得系统可以适应新需求的变化,同时不需要修改现有代码。这样就满足了开闭原则。
-
实现细节对客户端透明:桥接模式将实现部分对客户端隐藏,客户端只需关注抽象部分,降低了客户端与实现部分的耦合。
缺点:
-
增加了系统的复杂性:桥接模式引入了抽象和实现之间的桥接关系,这可能会增加系统的复杂性,特别是在理解和设计阶段。
-
学习和理解成本较高:对于初学者来说,桥接模式可能不是那么容易理解,需要时间去学习和掌握这种设计模式。
总的来说,桥接模式在处理多层次继承结构和需要独立扩展的场景下具有较好的优势,但同时也带来了一定程度的复杂性。在实际项目中,根据具体需求和场景权衡是否使用桥接模式。
转载自:https://juejin.cn/post/7224382896626253884