什么是桥接模式?
桥接模式是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立变化。桥接模式通过组合关系代替继承关系,降低了抽象和实现这两个可变维度的耦合度。
桥接模式包含以下角色:
- 抽象部分(Abstract):定义抽象接口,通常包含指向实现部分的引用
- 细化抽象部分(Refined Abstract):扩展抽象部分,提供更具体的实现
- 实现部分(Implementor):定义实现接口,通常是一个接口或抽象类
- 具体实现部分(Concrete Implementor):实现Implementor接口的具体实现
桥接模式的优缺点
优点:
- 分离抽象和实现:抽象部分和实现部分可以独立开发和变化
- 提高可扩展性:可以独立地扩展抽象部分和实现部分
符合开闭原则:新增抽象或实现时无需修改现有代码 - 隐藏实现细节:客户端不需要关心具体实现细节
- 减少子类数量:避免了多重继承导致的子类爆炸问题
缺点:
- 增加系统复杂度:需要正确识别系统中两个独立变化的维度
- 设计难度增加:需要在设计阶段就识别出抽象和实现部分
- 可能影响性能:通过组合方式实现功能,可能比直接继承方式稍慢
什么场景下使用桥接模式
- 不希望在抽象和实现部分之间有固定的绑定关系
- 类的抽象和实现都应该可以通过生成子类的方法加以扩充
- 对一个抽象的实现部分的修改应对客户不产生影响
- 想在带有不同抽象接口的多个对象之间共享实现
- 想在运行时刻切换不同的实现
代码举例
其实在实际开发当中能用到的桥接模式还说很多的,比如spring中的jdbc,日志框架等一些经典的开源框架中都有用到。我认为想真正在实际开发当中用到这些设计模式代码到例子必须在一个功能下举例更好,下面简单以日志输出举例
/ 实现部分接口 - 日志输出API
public interface LogAppender {void append(String level, String message, Throwable throwable);void append(String level, String message);void flush();void close();
}// 具体实现部分 - 控制台日志输出
public class ConsoleLogAppender implements LogAppender {private PrintWriter writer;public ConsoleLogAppender() {this.writer = new PrintWriter(System.out, true);}@Overridepublic void append(String level, String message, Throwable throwable) {writer.printf("[%s] %s%n", level, message);if (throwable != null) {throwable.printStackTrace(writer);}writer.flush();}@Overridepublic void append(String level, String message) {append(level, message, null);}@Overridepublic void flush() {writer.flush();}@Overridepublic void close() {writer.close();}
}// 具体实现部分 - 文件日志输出
public class FileLogAppender implements LogAppender {private PrintWriter writer;private String fileName;public FileLogAppender(String fileName) {this.fileName = fileName;try {this.writer = new PrintWriter(new FileWriter(fileName, true), true);} catch (IOException e) {throw new RuntimeException("无法创建日志文件: " + fileName, e);}}@Overridepublic void append(String level, String message, Throwable throwable) {String timestamp = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date());writer.printf("[%s] [%s] %s%n", timestamp, level, message);if (throwable != null) {throwable.printStackTrace(writer);}}@Overridepublic void append(String level, String message) {append(level, message, null);}@Overridepublic void flush() {writer.flush();}@Overridepublic void close() {writer.close();}public String getFileName() {return fileName;}
}// 具体实现部分 - 数据库日志输出
public class DatabaseLogAppender implements LogAppender {private DataSource dataSource;private String tableName;public DatabaseLogAppender(DataSource dataSource, String tableName) {this.dataSource = dataSource;this.tableName = tableName;}@Overridepublic void append(String level, String message, Throwable throwable) {String sql = "INSERT INTO " + tableName + " (log_level, log_message, log_time, throwable_info) VALUES (?, ?, ?, ?)";try (Connection conn = dataSource.getConnection();PreparedStatement stmt = conn.prepareStatement(sql)) {stmt.setString(1, level);stmt.setString(2, message);stmt.setTimestamp(3, new Timestamp(System.currentTimeMillis()));stmt.setString(4, throwable != null ? throwable.toString() : null);stmt.executeUpdate();} catch (SQLException e) {// 为避免循环日志,这里简单处理System.err.println("数据库日志写入失败: " + e.getMessage());}}@Overridepublic void append(String level, String message) {append(level, message, null);}@Overridepublic void flush() {// 数据库操作自动提交,无需特殊处理}@Overridepublic void close() {// 数据库连接由连接池管理,这里不关闭}
}// 抽象部分 - 日志记录器
public abstract class Logger {protected LogAppender appender;protected String name;protected boolean enabled;public Logger(String name, LogAppender appender) {this.name = name;this.appender = appender;this.enabled = true;}public abstract void debug(String message);public abstract void info(String message);public abstract void warn(String message);public abstract void error(String message);public abstract void error(String message, Throwable throwable);public void setEnabled(boolean enabled) {this.enabled = enabled;}public boolean isEnabled() {return enabled;}protected void log(String level, String message) {if (enabled) {appender.append(level, String.format("[%s] %s", name, message));}}protected void log(String level, String message, Throwable throwable) {if (enabled) {appender.append(level, String.format("[%s] %s", name, message), throwable);}}
}// 细化抽象部分 - 简单日志记录器
public class SimpleLogger extends Logger {public SimpleLogger(String name, LogAppender appender) {super(name, appender);}@Overridepublic void debug(String message) {log("DEBUG", message);}@Overridepublic void info(String message) {log("INFO", message);}@Overridepublic void warn(String message) {log("WARN", message);}@Overridepublic void error(String message) {log("ERROR", message);}@Overridepublic void error(String message, Throwable throwable) {log("ERROR", message, throwable);}
}// 细化抽象部分 - 带级别的日志记录器
public class LevelLogger extends Logger {public enum LogLevel {DEBUG(0), INFO(1), WARN(2), ERROR(3);private final int level;LogLevel(int level) {this.level = level;}public int getLevel() {return level;}}private LogLevel currentLevel = LogLevel.INFO;public LevelLogger(String name, LogAppender appender) {super(name, appender);}public void setLevel(LogLevel level) {this.currentLevel = level;}@Overridepublic void debug(String message) {if (currentLevel.getLevel() <= LogLevel.DEBUG.getLevel()) {log("DEBUG", message);}}@Overridepublic void info(String message) {if (currentLevel.getLevel() <= LogLevel.INFO.getLevel()) {log("INFO", message);}}@Overridepublic void warn(String message) {if (currentLevel.getLevel() <= LogLevel.WARN.getLevel()) {log("WARN", message);}}@Overridepublic void error(String message) {if (currentLevel.getLevel() <= LogLevel.ERROR.getLevel()) {log("ERROR", message);}}@Overridepublic void error(String message, Throwable throwable) {if (currentLevel.getLevel() <= LogLevel.ERROR.getLevel()) {log("ERROR", message, throwable);}}
}// 工具类工厂 - 简化使用
public class LoggerFactory {private static final Map<String, Logger> loggerCache = new ConcurrentHashMap<>();public static Logger getLogger(String name, String appenderType, Object... config) {String key = name + "_" + appenderType;return loggerCache.computeIfAbsent(key, k -> createLogger(name, appenderType, config));}public static Logger getLogger(Class<?> clazz, String appenderType, Object... config) {return getLogger(clazz.getName(), appenderType, config);}private static Logger createLogger(String name, String appenderType, Object... config) {LogAppender appender = createAppender(appenderType, config);// 可以根据需要选择不同类型的Loggerreturn new LevelLogger(name, appender);}private static LogAppender createAppender(String appenderType, Object... config) {switch (appenderType.toLowerCase()) {case "console":return new ConsoleLogAppender();case "file":if (config.length > 0 && config[0] instanceof String) {return new FileLogAppender((String) config[0]);} else {return new FileLogAppender("application.log");}case "database":if (config.length >= 2 && config[0] instanceof DataSource && config[1] instanceof String) {return new DatabaseLogAppender((DataSource) config[0], (String) config[1]);} else {throw new IllegalArgumentException("数据库日志需要DataSource和表名参数");}default:return new ConsoleLogAppender();}}
}// 客户端使用示例
public class LoggerDemo {public static void main(String[] args) {// 开发环境使用控制台日志Logger consoleLogger = LoggerFactory.getLogger("UserService", "console");consoleLogger.info("用户服务启动");consoleLogger.debug("调试信息");consoleLogger.error("发生错误", new RuntimeException("测试异常"));// 生产环境使用文件日志Logger fileLogger = LoggerFactory.getLogger("OrderService", "file", "order-service.log");fileLogger.info("订单服务启动");fileLogger.warn("订单处理警告");// 审计日志使用数据库存储DataSource dataSource = ... // 获取数据源Logger dbLogger = LoggerFactory.getLogger("AuditService", "database", dataSource, "audit_log");dbLogger.info("用户登录审计");// 测试日志级别控制LevelLogger levelLogger = (LevelLogger) LoggerFactory.getLogger("TestService", "console");levelLogger.setLevel(LevelLogger.LogLevel.WARN);levelLogger.debug("这条DEBUG日志不会输出");levelLogger.info("这条INFO日志不会输出");levelLogger.warn("这条WARN日志会输出");levelLogger.error("这条ERROR日志会输出");}
}