Bridge(桥接)
1)意图
将抽象部分与其实现部分分离,使它们都可以独立地变化。
2)结构
桥接模式的结构如图 7-32 所示。
其中:
- Abstraction 定义抽象类的接口,维护一个指向 Implementor 类型对象的指针。
- RefinedAbstraction 扩充由Abstraction 定义的接口。
- Implementor 定义实现类的接口,该接口不一定要与 Abstraction 的接口完全一致;事实上这两个接口可以完全不同。一般来说,Implementor 接口仅提供基本操作,而Abstraction 定义了基于这些基本操作的较高层次的操作。
- Concretelmplementor 实现 Implementor 接口并定义它的具体实现。
3)适用性
Bridge 模式适用于:
- 不希望在抽象和它的实现部分之间有一个固定的绑定关系。例如,这种情况可能是因为,在程序运行时刻实现部分应可以被选择或者切换。
- 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。这是 Bridge 模式使得开发者可以对不同的抽象接口和实现部分进行组合,并分别对它们进行扩充。
- 对一个抽象的实现部分的修改应对客户不产生影响,即客户代码不必重新编译。
- (C++)想对客户完全隐藏抽象的实现部分。
- 有许多类要生成的类层次结构。
- 想在多个对象间共享实现(可能使用引用计数),但同时要求客户并不知道这一点。
4 应用举例
1. 数据访问层(DAO)与数据库实现
在Web应用中,数据访问层(DAO)通常需要与不同的数据库进行交互。使用桥接模式可以将数据访问的抽象与具体的数据库实现分离,从而提高代码的灵活性和可维护性。
- 定义数据访问接口:
// 数据访问接口
public interface DataAccess {void save(Object entity);Object findById(String id);
}
- 实现具体的数据访问:
// MySQL数据访问实现
public class MySQLDataAccess implements DataAccess {@Overridepublic void save(Object entity) {System.out.println("Saving to MySQL: " + entity);}@Overridepublic Object findById(String id) {System.out.println("Finding by ID in MySQL: " + id);return new Object();}
}// PostgreSQL数据访问实现
public class PostgreSQLDataAccess implements DataAccess {@Overridepublic void save(Object entity) {System.out.println("Saving to PostgreSQL: " + entity);}@Overridepublic Object findById(String id) {System.out.println("Finding by ID in PostgreSQL: " + id);return new Object();}
}
- 定义抽象的DAO:
// 抽象的DAO
public abstract class AbstractDAO {protected DataAccess dataAccess;public AbstractDAO(DataAccess dataAccess) {this.dataAccess = dataAccess;}public abstract void saveEntity(Object entity);public abstract Object findEntityById(String id);
}
- 实现具体的DAO:
// 用户DAO
public class UserDAO extends AbstractDAO {public UserDAO(DataAccess dataAccess) {super(dataAccess);}@Overridepublic void saveEntity(Object entity) {dataAccess.save(entity);}@Overridepublic Object findEntityById(String id) {return dataAccess.findById(id);}
}
- 测试桥接模式:
public class Main {public static void main(String[] args) {DataAccess mysqlDataAccess = new MySQLDataAccess();DataAccess postgresqlDataAccess = new PostgreSQLDataAccess();UserDAO userDAO1 = new UserDAO(mysqlDataAccess);userDAO1.saveEntity(new Object());userDAO1.findEntityById("123");UserDAO userDAO2 = new UserDAO(postgresqlDataAccess);userDAO2.saveEntity(new Object());userDAO2.findEntityById("456");}
}
2. 日志记录与日志实现
在Web应用中,日志记录是一个常见的功能。使用桥接模式可以将日志记录的抽象与具体的日志实现分离,从而支持多种日志记录方式(如文件日志、数据库日志、网络日志等)。
- 定义日志记录接口:
// 日志记录接口
public interface Logger {void log(String message);
}
- 实现具体的日志记录:
// 文件日志记录实现
public class FileLogger implements Logger {@Overridepublic void log(String message) {System.out.println("Logging to file: " + message);}
}// 数据库日志记录实现
public class DatabaseLogger implements Logger {@Overridepublic void log(String message) {System.out.println("Logging to database: " + message);}
}
- 定义抽象的日志记录器:
// 抽象的日志记录器
public abstract class AbstractLogger {protected Logger logger;public AbstractLogger(Logger logger) {this.logger = logger;}public abstract void logMessage(String message);
}
- 实现具体的日志记录器:
// 应用日志记录器
public class ApplicationLogger extends AbstractLogger {public ApplicationLogger(Logger logger) {super(logger);}@Overridepublic void logMessage(String message) {logger.log(message);}
}
- 测试桥接模式:
public class Main {public static void main(String[] args) {Logger fileLogger = new FileLogger();Logger databaseLogger = new DatabaseLogger();ApplicationLogger appLogger1 = new ApplicationLogger(fileLogger);appLogger1.logMessage("This is a file log message.");ApplicationLogger appLogger2 = new ApplicationLogger(databaseLogger);appLogger2.logMessage("This is a database log message.");}
}
3. 用户界面与渲染实现
在Web应用中,用户界面的渲染可能需要支持多种不同的前端技术(如HTML、JSON、XML等)。使用桥接模式可以将用户界面的抽象与具体的渲染实现分离,从而支持多种渲染方式。
- 定义渲染接口:
// 渲染接口
public interface Renderer {void render(Object data);
}
- 实现具体的渲染:
// HTML渲染实现
public class HtmlRenderer implements Renderer {@Overridepublic void render(Object data) {System.out.println("Rendering as HTML: " + data);}
}// JSON渲染实现
public class JsonRenderer implements Renderer {@Overridepublic void render(Object data) {System.out.println("Rendering as JSON: " + data);}
}
- 定义抽象的用户界面:
// 抽象的用户界面
public abstract class AbstractUI {protected Renderer renderer;public AbstractUI(Renderer renderer) {this.renderer = renderer;}public abstract void display(Object data);
}
- 实现具体的用户界面:
// 用户界面
public class UserInterface extends AbstractUI {public UserInterface(Renderer renderer) {super(renderer);}@Overridepublic void display(Object data) {renderer.render(data);}
}
- 测试桥接模式:
public class Main {public static void main(String[] args) {Renderer htmlRenderer = new HtmlRenderer();Renderer jsonRenderer = new JsonRenderer();UserInterface ui1 = new UserInterface(htmlRenderer);ui1.display(new Object());UserInterface ui2 = new UserInterface(jsonRenderer);ui2.display(new Object());}
}
桥接模式在Web开发中非常有用,特别是在需要支持多种实现方式的场景下。通过将抽象部分与实现部分分离,桥接模式可以提高代码的灵活性和可维护性,减少类的膨胀,使系统更加模块化和易于扩展。上述三个案例分别展示了在数据访问、日志记录和用户界面渲染中的应用,希望这些示例能帮助你更好地理解和应用桥接模式。