当抽象与实现,各自独立, 桥接模式,如彩虹桥,连接两岸。
文章目录
一、类爆炸与代码重复
场景假设:假设我们正在设计一个模拟城市交通的系统。在这个系统中,我们有各种类型的桥梁,如悬索桥和拱桥,以及各种类型的车辆,如汽车和自行车。我们希望能够模拟各种车辆在各种桥梁上行驶的情况。
java
// SuspensionBridgeCar 类代表在悬索桥上驾驶汽车的情况
class SuspensionBridgeCar {
public void drive() {
System.out.println("Driving a car on a suspension bridge");
}
}
// ArchBridgeCar 类代表在拱桥上驾驶汽车的情况
class ArchBridgeCar {
public void drive() {
System.out.println("Driving a car on an arch bridge");
}
}
// SuspensionBridgeBicycle 类代表在悬索桥上骑自行车的情况
class SuspensionBridgeBicycle {
public void drive() {
System.out.println("Riding a bicycle on a suspension bridge");
}
}
// ArchBridgeBicycle 类代表在拱桥上骑自行车的情况
class ArchBridgeBicycle {
public void drive() {
System.out.println("Riding a bicycle on an arch bridge");
}
}
public class Main {
public static void main(String[] args) {
// 创建一个 SuspensionBridgeCar 对象来模拟在悬索桥上驾驶汽车的情况
SuspensionBridgeCar suspensionBridgeCar = new SuspensionBridgeCar();
suspensionBridgeCar.drive(); // 输出: "Driving a car on a suspension bridge"
// 创建一个 ArchBridgeBicycle 对象来模拟在拱桥上骑自行车的情况
ArchBridgeBicycle archBridgeBicycle = new ArchBridgeBicycle();
archBridgeBicycle.drive(); // 输出: "Riding a bicycle on an arch bridge"
// 如果我们想要添加新的桥梁类型或车辆类型,我们需要创建更多的类
// 例如,如果我们想要添加"跳绳桥"和"摩托车",我们需要创建四个新的类:
// JumpRopeBridgeCar, JumpRopeBridgeBicycle, SuspensionBridgeMotorcycle, ArchBridgeMotorcycle
}
}
在这个例子中,我们可以看到以下问题:
- 类的数量爆炸:我们需要为每种桥梁和车辆的组合创建一个新的类。这使得代码难以管理和维护。
- 代码重复:在每个子类中,我们可能需要重复相同的代码,这违反了 DRY(Don`t Repeat Yourself)原则。
二、桥接模式
桥接模式是一种结构型
设计模式,它主要解决的是"将抽象部分与实现部分分离,使它们可以独立地变化"。它通过提供一个桥接结构,把抽象和实现解耦,使得二者可以独立地变化和复用。
在桥接模式中,有两个独立变化的维度:抽象化(Abstraction)和实现化(Implementation)。抽象化是主要的业务逻辑,而实现化是抽象化依赖的底层实现。桥接模式的目标是将这两个维度分离,使它们可以独立地变化和复用,而不是创建一个包含所有可能组合的类。
三、桥接模式的核心组成
桥接模式由以下几个关键角色组成:
- 抽象类(Abstraction): 定义了抽象部分的接口,并包含一个指向实现部分对象的引用。抽象类可以包含一些基本操作,而具体的实现则由实现部分提供。
- 扩展抽象类(Refined Abstraction): 是对抽象类的扩展,可以添加更多的功能或细化抽象类定义的接口。扩展抽象类通过调用实现部分对象的方法来实现具体的功能。
- 实现接口(Implementor): 定义了实现部分的接口,通常包含一组操作方法。实现接口不关心抽象部分的接口,它只负责实现具体的功能。
- 具体实现类(Concrete Implementor): 实现了实现接口的具体功能。具体实现类负责实现实现接口定义的操作方法,并与抽象部分的接口进行对应。
在这个类图中:
Abstraction
是抽象化角色,它定义了基于 Implementor 接口(实现化角色)的高级操作。RefinedAbstraction
是扩展抽象化角色,它提供了对 Implementor 的具体实现。Implementor
是实现化角色,它定义了抽象化角色依赖的底层操作。ConcreteImplementorA
和ConcreteImplementorB
是具体实现化角色,它们实现了 Implementor 接口。
四、运用桥接模式
场景假设:假设我们正在设计一个模拟城市交通的系统。在这个系统中,我们有各种类型的桥梁,如悬索桥和拱桥,以及各种类型的车辆,如汽车和自行车。我们希望能够模拟各种车辆在各种桥梁上行驶的情况。
-
创建实现接口: 在桥接模式中,首先我们需要创建一个实现接口,该接口定义了实现部分的操作方法。
javainterface Vehicle { // 这个接口定义了一些方法,这些方法将在具体的实现类中实现。 void operate(); }
-
创建具体实现类: 接着,我们需要创建具体实现类,实现实现接口中定义的具体功能。
java// `Car`实现了`Vehicle`接口,它提供了`operate`方法的具体实现。 class Car implements Vehicle { public void operate() { System.out.println("car"); } } // `Bicycle`也实现了`Vehicle`接口,它的实现方式与`Car`类似。 class Bicycle implements Vehicle { public void operate() { System.out.println("bicycle"); } }
-
创建抽象类: 接下来,我们创建一个抽象类,该类将持有一个实现接口的引用,并提供一些操作方法,这些方法将委托给实现接口来实现具体的功能。
javaabstract class Bridge { // 这个类包含一个`Vehicle`类型的成员变量,这个变量是实现部分的接口。 protected Vehicle vehicle; // 构造函数接收一个`Vehicle`对象,并将其赋值给成员变量。 public Bridge(Vehicle vehicle) { this.vehicle = vehicle; } // 定义抽象方法`drive`,具体的实现将在子类中完成。 public abstract void drive(); }
-
创建具体实现类: 进一步,我们创建具体实现类,继承自抽象类,并实现抽象类中的抽象方法。
java// `SuspensionBridge`是`Bridge`的子类,它实现了`drive`方法。 class SuspensionBridge extends Bridge { public SuspensionBridge(Vehicle vehicle) { super(vehicle); } public void drive() { System.out.print("Driving on a suspension bridge in a "); vehicle.operate(); } } // `ArchBridge`也是`Bridge`的子类,它的实现方式与`SuspensionBridge`类似。 class ArchBridge extends Bridge { public ArchBridge(Vehicle vehicle) { super(vehicle); } public void drive() { System.out.print("Driving on an arch bridge in a "); vehicle.operate(); } }
-
在客户端使用桥接模式: 最后,在客户端代码中使用桥接模式,通过创建具体的对象并调用其方法来操作电视。
java// 在客户端代码中,我们创建了一个`SuspensionBridge`对象和一个`Car`对象,并将它们组合在一起。 public class Main { public static void main(String[] args) { Bridge bridge = new SuspensionBridge(new Car()); bridge.drive(); // Output: "Driving on a suspension bridge in a car" } }
通过上面的桥接模式,通过将抽象(桥梁)和实现(车辆)分离,使它们可以独立地变化。这样,可以更灵活地添加新的桥梁或车辆,而不需要为每种组合创建一个新的类。
五、桥接模式的应用场景
桥接模式主要适用于以下几种场景:
- 独立变化的维度:当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时,可以使用桥接模式。
- 不希望使用多重继承或因多重继承导致的类爆炸:桥接模式可以替代多重继承方案,它减少了子类的个数。
- 接口或抽象类不稳定:如果系统的抽象部分和具体部分需要独立地变化,那么桥接模式可以保证系统的持续运行。
- 重用性要求较高:桥接模式中的抽象部分和实现部分都可以独立地扩展,这有助于提高系统的重用性。
以下是一些具体的应用场景:
-
跨平台图形和文字处理软件:在这种软件中,形状和颜色、字体和字号等因素可以独立变化,使用桥接模式可以将这些因素的实现从抽象中分离出来,使得它们可以独立地变化。
java// 形状的抽象类 abstract class Shape { protected Color color; protected Shape(Color color) { this.color = color; } abstract void draw(); } // 圆形 class Circle extends Shape { protected Circle(Color color) { super(color); } void draw() { System.out.println("Draw a circle with " + color.getColor()); } } // 矩形 class Rectangle extends Shape { protected Rectangle(Color color) { super(color); } void draw() { System.out.println("Draw a rectangle with " + color.getColor()); } } // 颜色的接口 interface Color { String getColor(); } // 红色 class Red implements Color { public String getColor() { return "red"; } } // 蓝色 class Blue implements Color { public String getColor() { return "blue"; } } public static void main(String[] args) { Shape redCircle = new Circle(new Red()); redCircle.draw(); Shape blueRectangle = new Rectangle(new Blue()); blueRectangle.draw(); }
-
驱动程序:驱动程序中的操作系统和硬件设备是两个独立变化的维度,使用桥接模式可以将它们分离,使得操作系统可以在不同的硬件设备上运行,同时硬件设备也可以接入不同的操作系统。
java// 操作系统的抽象类 abstract class OperatingSystem { protected Device device; protected OperatingSystem(Device device) { this.device = device; } abstract void run(); } // Windows 操作系统 class Windows extends OperatingSystem { protected Windows(Device device) { super(device); } void run() { System.out.println("Run Windows on " + device.getDevice()); } } // Linux 操作系统 class Linux extends OperatingSystem { protected Linux(Device device) { super(device); } void run() { System.out.println("Run Linux on " + device.getDevice()); } } // 硬件设备的接口 interface Device { String getDevice(); } // Dell 设备 class Dell implements Device { public String getDevice() { return "Dell"; } } // HP 设备 class HP implements Device { public String getDevice() { return "HP"; } } public static void main(String[] args) { OperatingSystem windowsOnDell = new Windows(new Dell()); windowsOnDell.run(); OperatingSystem linuxOnHP = new Linux(new HP()); linuxOnHP.run(); }
-
数据库连接:在数据库连接中,数据库类型(如 MySQL、Oracle)和连接方式(如 ODBC、JDBC)是两个独立变化的维度,使用桥接模式可以将它们分离,使得可以灵活地组合不同的数据库类型和连接方式。
java// 数据库的抽象类 abstract class Database { protected Connection connection; protected Database(Connection connection) { this.connection = connection; } abstract void connect(); } // MySQL 数据库 class MySQL extends Database { protected MySQL(Connection connection) { super(connection); } void connect() { System.out.println("Connect to MySQL with " + connection.getConnection()); } } // Oracle 数据库 class Oracle extends Database { protected Oracle(Connection connection) { super(connection); } void connect() { System.out.println("Connect to Oracle with " + connection.getConnection()); } } // 连接方式的接口 interface Connection { String getConnection(); } // ODBC 连接 class ODBC implements Connection { public String getConnection() { return "ODBC"; } } // JDBC 连接 class JDBC implements Connection { public String getConnection() { return "JDBC"; } } public static void main(String[] args) { Database mySqlWithODBC = new MySQL(new ODBC()); mySqlWithODBC.connect(); Database oracleWithJDBC = new Oracle(new JDBC()); oracleWithJDBC.connect(); }
六、小结
桥接模式是一种强大的设计模式,它能够有效地解耦抽象和实现,使系统更加灵活和可扩展。通过将抽象部分和实现部分分离,桥接模式使得我们可以轻松地扩展和变化系统的功能,而不会影响原有的代码结构。在实际应用中,我们可以根据具体的需求来选择是否使用桥接模式,以达到更好的设计和开发效果。