深入理解设计模式之桥接模式(Bridge Pattern)
一、引言
在软件开发中,我们经常遇到需要在多个维度上扩展的场景。比如:图形既有形状维度(圆形、矩形、三角形),又有颜色维度(红色、蓝色、绿色);消息既有类型维度(文本、图片、视频),又有发送方式维度(邮件、短信、推送)。
如果使用传统的继承方式,会导致类的数量爆炸性增长:
传统继承方式的问题:
图形
├── 红色圆形
├── 蓝色圆形
├── 绿色圆形
├── 红色矩形
├── 蓝色矩形
├── 绿色矩形
├── 红色三角形
├── 蓝色三角形
└── 绿色三角形
3种形状 × 3种颜色 = 9个类!
如果再加一种形状或颜色,类的数量继续增加...
桥接模式为这类问题提供了优雅的解决方案。它就像现实中的遥控器和电视:遥控器(抽象)和电视机(实现)是分离的,可以用一个遥控器控制不同品牌的电视,也可以用不同的遥控器控制同一个电视。这种分离让两者可以独立变化。
本文将深入探讨桥接模式的原理、实现方式,并结合JDBC、日志框架等实际应用,帮助你全面掌握这一重要的设计模式。
二、什么是桥接模式
2.1 定义
桥接模式(Bridge Pattern)是一种结构型设计模式,它将抽象部分与实现部分分离,使它们都可以独立变化。桥接模式通过组合的方式建立两个类之间的联系,而不是继承。
2.2 核心思想
- 分离抽象和实现:将抽象(业务功能)和实现(平台实现)分离
- 组合优于继承:使用组合关系代替继承关系
- 两个维度独立变化:抽象和实现可以独立扩展
- 减少子类数量:避免类的爆炸式增长
2.3 模式结构
桥接模式结构:
┌─────────────────────────┐
│ <<interface>> │
│ Abstraction │ 抽象类
├─────────────────────────┤
│ - implementor │◇────┐ 关联关系(桥接)
│ + operation() │ │
└───────────┬─────────────┘ │
△ │
│ 继承 │
┌──────┴──────┐ │
│ │ │
┌────┴─────┐ ┌───┴──────┐ │
│RefinedA │ │RefinedB │ │
│Abstraction Abstraction │ │
└──────────┘ └──────────┘ │
│
│
▼
┌──────────────────────┐
│ <<interface>> │
│ Implementor │ 实现接口
├──────────────────────┤
│ + operationImpl() │
└──────────┬───────────┘
△
│ 实现
┌──────┴──────┐
│ │
┌───────┴──┐ ┌────┴──────┐
│ConcreteA │ │ConcreteB │ 具体实现
│Implementor │Implementor│
└──────────┘ └───────────┘
调用流程:
Abstraction.operation() → Implementor.operationImpl()
抽象调用实现的方法
对比:不使用桥接模式
┌──────────┐
│ Shape │
└────┬─────┘
│
┌──┴────────────┐
│ │
┌─┴──┐ ┌──┴───┐
│Red │ │Blue │
│Shape │Shape │
└─┬──┘ └──┬───┘
│ │
┌─┴───────┐ ┌──┴────────┐
│RedCircle│ │BlueCircle │
│RedSquare│ │BlueSquare │
└─────────┘ └───────────┘
类的数量 = 形状数 × 颜色数
使用桥接模式:
Shape (持有Color引用)
Color (独立的颜色实现)
类的数量 = 形状数 + 颜色数
2.4 桥接模式的应用场景
1. 多维度变化
- 形状 + 颜色
- 平台 + 业务
- 消息类型 + 发送方式
2. 避免继承层次过深
- 减少子类数量
- 降低系统复杂度
3. 需要在运行时切换实现
- 动态选择实现方式
- 不修改客户端代码
三、基础示例
3.1 场景:图形和颜色
经典的桥接模式示例:形状和颜色是两个独立的维度。
实现部分(颜色):
java
/**
* 实现接口:颜色
*/
public interface Color {
/**
* 填充颜色
*/
void fill();
}
/**
* 具体实现:红色
*/
public class Red implements Color {
@Override
public void fill() {
System.out.println("填充红色");
}
}
/**
* 具体实现:蓝色
*/
public class Blue implements Color {
@Override
public void fill() {
System.out.println("填充蓝色");
}
}
/**
* 具体实现:绿色
*/
public class Green implements Color {
@Override
public void fill() {
System.out.println("填充绿色");
}
}
抽象部分(形状):
java
/**
* 抽象类:形状
*/
public abstract class Shape {
// 持有颜色实现的引用(桥接)
protected Color color;
/**
* 构造函数注入颜色实现
*/
public Shape(Color color) {
this.color = color;
}
/**
* 抽象方法:绘制形状
*/
public abstract void draw();
}
/**
* 扩展抽象:圆形
*/
public class Circle extends Shape {
private double radius;
public Circle(Color color, double radius) {
super(color);
this.radius = radius;
}
@Override
public void draw() {
System.out.print("绘制圆形,半径: " + radius + " - ");
color.fill(); // 调用实现部分的方法
}
}
/**
* 扩展抽象:矩形
*/
public class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(Color color, double width, double height) {
super(color);
this.width = width;
this.height = height;
}
@Override
public void draw() {
System.out.print("绘制矩形,宽: " + width + ", 高: " + height + " - ");
color.fill();
}
}
/**
* 扩展抽象:三角形
*/
public class Triangle extends Shape {
private double base;
private double height;
public Triangle(Color color, double base, double height) {
super(color);
this.base = base;
this.height = height;
}
@Override
public void draw() {
System.out.print("绘制三角形,底: " + base + ", 高: " + height + " - ");
color.fill();
}
}
客户端使用:
java
public class BridgePatternDemo {
public static void main(String[] args) {
// 创建不同颜色的圆形
System.out.println("=== 不同颜色的圆形 ===");
Shape redCircle = new Circle(new Red(), 5.0);
Shape blueCircle = new Circle(new Blue(), 7.0);
Shape greenCircle = new Circle(new Green(), 3.0);
redCircle.draw();
blueCircle.draw();
greenCircle.draw();
// 创建不同颜色的矩形
System.out.println("\n=== 不同颜色的矩形 ===");
Shape redRectangle = new Rectangle(new Red(), 10.0, 5.0);
Shape blueRectangle = new Rectangle(new Blue(), 8.0, 6.0);
redRectangle.draw();
blueRectangle.draw();
// 创建不同颜色的三角形
System.out.println("\n=== 不同颜色的三角形 ===");
Shape greenTriangle = new Triangle(new Green(), 4.0, 6.0);
Shape redTriangle = new Triangle(new Red(), 5.0, 8.0);
greenTriangle.draw();
redTriangle.draw();
// 优势:添加新的形状或颜色非常容易
// 添加新颜色:只需实现Color接口
// 添加新形状:只需继承Shape类
// 类的数量:3个形状 + 3个颜色 = 6个类
// 而不是:3 × 3 = 9个类
}
}
输出:
=== 不同颜色的圆形 ===
绘制圆形,半径: 5.0 - 填充红色
绘制圆形,半径: 7.0 - 填充蓝色
绘制圆形,半径: 3.0 - 填充绿色
=== 不同颜色的矩形 ===
绘制矩形,宽: 10.0, 高: 5.0 - 填充红色
绘制矩形,宽: 8.0, 高: 6.0 - 填充蓝色
=== 不同颜色的三角形 ===
绘制三角形,底: 4.0, 高: 6.0 - 填充绿色
绘制三角形,底: 5.0, 高: 8.0 - 填充红色
四、实际生产场景应用
4.1 场景:消息发送系统
在实际系统中,消息有多种类型(文本、图片、视频),发送方式也有多种(邮件、短信、APP推送)。
实现部分(发送方式):
java
/**
* 实现接口:消息发送器
*/
public interface MessageSender {
/**
* 发送消息
* @param recipient 接收者
* @param content 内容
*/
void send(String recipient, String content);
}
/**
* 具体实现:邮件发送
*/
public class EmailSender implements MessageSender {
@Override
public void send(String recipient, String content) {
System.out.println("=== 邮件发送 ===");
System.out.println("收件人: " + recipient);
System.out.println("内容: " + content);
System.out.println("通过SMTP服务器发送邮件");
System.out.println();
}
}
/**
* 具体实现:短信发送
*/
public class SmsSender implements MessageSender {
@Override
public void send(String recipient, String content) {
System.out.println("=== 短信发送 ===");
System.out.println("手机号: " + recipient);
System.out.println("内容: " + content);
System.out.println("通过短信网关发送");
System.out.println();
}
}
/**
* 具体实现:APP推送
*/
public class AppPushSender implements MessageSender {
@Override
public void send(String recipient, String content) {
System.out.println("=== APP推送 ===");
System.out.println("用户ID: " + recipient);
System.out.println("内容: " + content);
System.out.println("通过推送服务发送");
System.out.println();
}
}
/**
* 具体实现:企业微信发送
*/
public class WechatWorkSender implements MessageSender {
@Override
public void send(String recipient, String content) {
System.out.println("=== 企业微信 ===");
System.out.println("用户: " + recipient);
System.out.println("内容: " + content);
System.out.println("通过企业微信API发送");
System.out.println();
}
}
抽象部分(消息类型):
java
/**
* 抽象类:消息
*/
public abstract class Message {
// 持有发送器的引用(桥接)
protected MessageSender sender;
public Message(MessageSender sender) {
this.sender = sender;
}
/**
* 发送消息(模板方法)
*/
public void sendMessage(String recipient) {
// 1. 格式化内容(由子类实现)
String content = formatContent();
// 2. 发送前置处理
preProcess(recipient);
// 3. 使用发送器发送
sender.send(recipient, content);
// 4. 发送后置处理
postProcess(recipient);
}
/**
* 格式化内容(由子类实现)
*/
protected abstract String formatContent();
/**
* 发送前置处理(钩子方法)
*/
protected void preProcess(String recipient) {
// 默认不做处理
}
/**
* 发送后置处理(钩子方法)
*/
protected void postProcess(String recipient) {
// 默认不做处理
}
}
/**
* 扩展抽象:文本消息
*/
public class TextMessage extends Message {
private String text;
public TextMessage(MessageSender sender, String text) {
super(sender);
this.text = text;
}
@Override
protected String formatContent() {
return "[文本消息] " + text;
}
@Override
protected void preProcess(String recipient) {
System.out.println("[前置] 检查文本消息是否包含敏感词");
}
}
/**
* 扩展抽象:图片消息
*/
public class ImageMessage extends Message {
private String imageUrl;
private String description;
public ImageMessage(MessageSender sender, String imageUrl, String description) {
super(sender);
this.imageUrl = imageUrl;
this.description = description;
}
@Override
protected String formatContent() {
return "[图片消息]\n" +
"图片URL: " + imageUrl + "\n" +
"描述: " + description;
}
@Override
protected void preProcess(String recipient) {
System.out.println("[前置] 检查图片是否存在且可访问");
}
}
/**
* 扩展抽象:紧急消息
*/
public class UrgentMessage extends Message {
private String title;
private String content;
public UrgentMessage(MessageSender sender, String title, String content) {
super(sender);
this.title = title;
this.content = content;
}
@Override
protected String formatContent() {
return "【紧急通知】" + title + "\n" + content;
}
@Override
protected void preProcess(String recipient) {
System.out.println("[前置] 记录紧急消息发送日志");
}
@Override
protected void postProcess(String recipient) {
System.out.println("[后置] 确认消息已送达");
}
}
客户端使用:
java
public class MessageBridgeDemo {
public static void main(String[] args) {
// 场景1:通过邮件发送文本消息
System.out.println("========== 场景1 ==========");
Message emailText = new TextMessage(
new EmailSender(),
"您好,这是一条测试消息"
);
emailText.sendMessage("user@example.com");
// 场景2:通过短信发送文本消息
System.out.println("========== 场景2 ==========");
Message smsText = new TextMessage(
new SmsSender(),
"您的验证码是:123456"
);
smsText.sendMessage("138****8888");
// 场景3:通过APP推送发送图片消息
System.out.println("========== 场景3 ==========");
Message appImage = new ImageMessage(
new AppPushSender(),
"https://example.com/image.jpg",
"新产品上线"
);
appImage.sendMessage("USER001");
// 场景4:通过企业微信发送紧急消息
System.out.println("========== 场景4 ==========");
Message urgentWechat = new UrgentMessage(
new WechatWorkSender(),
"系统告警",
"生产环境CPU使用率超过90%"
);
urgentWechat.sendMessage("运维团队");
// 优势:轻松组合不同的消息类型和发送方式
// 添加新的消息类型:只需继承Message
// 添加新的发送方式:只需实现MessageSender
}
}
4.2 场景:跨平台视频播放器
视频播放器需要支持多种平台(Windows、Mac、Linux)和多种格式(MP4、AVI、MKV)。
java
/**
* 实现接口:视频播放引擎
*/
public interface VideoPlayerEngine {
/**
* 初始化引擎
*/
void initialize();
/**
* 播放视频
*/
void play(String fileName);
/**
* 暂停播放
*/
void pause();
/**
* 停止播放
*/
void stop();
}
/**
* 具体实现:Windows播放引擎
*/
public class WindowsVideoEngine implements VideoPlayerEngine {
@Override
public void initialize() {
System.out.println("[Windows] 初始化DirectShow引擎");
}
@Override
public void play(String fileName) {
System.out.println("[Windows] 使用Windows Media Player播放: " + fileName);
}
@Override
public void pause() {
System.out.println("[Windows] 暂停播放");
}
@Override
public void stop() {
System.out.println("[Windows] 停止播放");
}
}
/**
* 具体实现:Mac播放引擎
*/
public class MacVideoEngine implements VideoPlayerEngine {
@Override
public void initialize() {
System.out.println("[Mac] 初始化AVFoundation引擎");
}
@Override
public void play(String fileName) {
System.out.println("[Mac] 使用QuickTime播放: " + fileName);
}
@Override
public void pause() {
System.out.println("[Mac] 暂停播放");
}
@Override
public void stop() {
System.out.println("[Mac] 停止播放");
}
}
/**
* 具体实现:Linux播放引擎
*/
public class LinuxVideoEngine implements VideoPlayerEngine {
@Override
public void initialize() {
System.out.println("[Linux] 初始化FFmpeg引擎");
}
@Override
public void play(String fileName) {
System.out.println("[Linux] 使用VLC播放: " + fileName);
}
@Override
public void pause() {
System.out.println("[Linux] 暂停播放");
}
@Override
public void stop() {
System.out.println("[Linux] 停止播放");
}
}
/**
* 抽象类:视频播放器
*/
public abstract class VideoPlayer {
protected VideoPlayerEngine engine;
public VideoPlayer(VideoPlayerEngine engine) {
this.engine = engine;
this.engine.initialize();
}
/**
* 播放视频
*/
public abstract void playVideo(String fileName);
}
/**
* 扩展抽象:高清播放器
*/
public class HDVideoPlayer extends VideoPlayer {
public HDVideoPlayer(VideoPlayerEngine engine) {
super(engine);
}
@Override
public void playVideo(String fileName) {
System.out.println("\n=== 高清播放模式 ===");
System.out.println("分辨率: 1920x1080");
System.out.println("比特率: 5000kbps");
engine.play(fileName);
}
}
/**
* 扩展抽象:标清播放器
*/
public class SDVideoPlayer extends VideoPlayer {
public SDVideoPlayer(VideoPlayerEngine engine) {
super(engine);
}
@Override
public void playVideo(String fileName) {
System.out.println("\n=== 标清播放模式 ===");
System.out.println("分辨率: 720x480");
System.out.println("比特率: 1500kbps");
engine.play(fileName);
}
}
/**
* 扩展抽象:流媒体播放器
*/
public class StreamingVideoPlayer extends VideoPlayer {
public StreamingVideoPlayer(VideoPlayerEngine engine) {
super(engine);
}
@Override
public void playVideo(String fileName) {
System.out.println("\n=== 流媒体播放模式 ===");
System.out.println("启用缓冲: 是");
System.out.println("自适应比特率: 开启");
engine.play(fileName);
}
}
/**
* 测试视频播放器
*/
class VideoPlayerDemo {
public static void main(String[] args) {
// Windows平台高清播放
System.out.println("========== Windows平台 ==========");
VideoPlayer windowsHD = new HDVideoPlayer(new WindowsVideoEngine());
windowsHD.playVideo("movie.mp4");
// Mac平台标清播放
System.out.println("\n========== Mac平台 ==========");
VideoPlayer macSD = new SDVideoPlayer(new MacVideoEngine());
macSD.playVideo("video.avi");
// Linux平台流媒体播放
System.out.println("\n========== Linux平台 ==========");
VideoPlayer linuxStream = new StreamingVideoPlayer(new LinuxVideoEngine());
linuxStream.playVideo("stream.mkv");
// 优势:
// 1. 3种播放器类型 + 3种平台引擎 = 6个类
// 2. 不使用桥接:3 × 3 = 9个类
// 3. 添加新平台或新播放器类型都很容易
}
}
五、开源框架中的应用
5.1 JDBC驱动程序
JDBC是桥接模式最经典的应用之一。
java
/**
* JDBC的桥接模式
*
* 抽象部分:JDBC API(java.sql包)
* - Connection
* - Statement
* - ResultSet
*
* 实现部分:各厂商的JDBC驱动
* - MySQL驱动
* - Oracle驱动
* - PostgreSQL驱动
*/
import java.sql.*;
public class JdbcBridgeExample {
/**
* JDBC使用示例
*/
public static void demonstrateJdbc() throws SQLException {
// 抽象:使用标准JDBC API
Connection conn = null;
// 实现:通过DriverManager选择具体驱动
// MySQL驱动
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydb",
"user",
"password"
);
// 或者Oracle驱动(只需更改URL,代码不变)
// conn = DriverManager.getConnection(
// "jdbc:oracle:thin:@localhost:1521:orcl",
// "user",
// "password"
// );
// 使用相同的JDBC API操作数据库
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users");
while (rs.next()) {
System.out.println(rs.getString("name"));
}
rs.close();
stmt.close();
conn.close();
}
}
/**
* 简化的JDBC桥接模式实现
*/
// 抽象部分:数据库连接
interface DatabaseConnection {
void connect(String url);
void executeQuery(String sql);
void close();
}
// 实现部分:数据库驱动接口
interface DatabaseDriver {
void establishConnection(String url);
void runQuery(String sql);
void disconnect();
}
// MySQL驱动实现
class MySqlDriver implements DatabaseDriver {
@Override
public void establishConnection(String url) {
System.out.println("[MySQL] 建立连接: " + url);
}
@Override
public void runQuery(String sql) {
System.out.println("[MySQL] 执行查询: " + sql);
}
@Override
public void disconnect() {
System.out.println("[MySQL] 断开连接");
}
}
// Oracle驱动实现
class OracleDriver implements DatabaseDriver {
@Override
public void establishConnection(String url) {
System.out.println("[Oracle] 建立连接: " + url);
}
@Override
public void runQuery(String sql) {
System.out.println("[Oracle] 执行查询: " + sql);
}
@Override
public void disconnect() {
System.out.println("[Oracle] 断开连接");
}
}
// 桥接实现
class JdbcConnection implements DatabaseConnection {
private DatabaseDriver driver;
public JdbcConnection(DatabaseDriver driver) {
this.driver = driver;
}
@Override
public void connect(String url) {
driver.establishConnection(url);
}
@Override
public void executeQuery(String sql) {
driver.runQuery(sql);
}
@Override
public void close() {
driver.disconnect();
}
}
/**
* 测试JDBC桥接
*/
class JdbcBridgeDemo {
public static void main(String[] args) {
// 使用MySQL驱动
System.out.println("=== 使用MySQL ===");
DatabaseConnection mysqlConn = new JdbcConnection(new MySqlDriver());
mysqlConn.connect("jdbc:mysql://localhost:3306/mydb");
mysqlConn.executeQuery("SELECT * FROM users");
mysqlConn.close();
// 切换到Oracle驱动(客户端代码不变)
System.out.println("\n=== 使用Oracle ===");
DatabaseConnection oracleConn = new JdbcConnection(new OracleDriver());
oracleConn.connect("jdbc:oracle:thin:@localhost:1521:orcl");
oracleConn.executeQuery("SELECT * FROM users");
oracleConn.close();
}
}
5.2 SLF4J日志框架
SLF4J(Simple Logging Facade for Java)使用桥接模式支持多种日志实现。
java
/**
* SLF4J的桥接模式
*
* 抽象部分:SLF4J API
* - Logger接口
* - LoggerFactory
*
* 实现部分:各种日志框架
* - Logback
* - Log4j
* - JDK Logging
*/
// 模拟SLF4J的桥接实现
/**
* 抽象部分:日志接口
*/
interface Logger {
void info(String message);
void debug(String message);
void error(String message);
}
/**
* 实现部分:日志实现接口
*/
interface LoggerImplementation {
void logInfo(String message);
void logDebug(String message);
void logError(String message);
}
/**
* Logback实现
*/
class LogbackImplementation implements LoggerImplementation {
@Override
public void logInfo(String message) {
System.out.println("[Logback] INFO - " + message);
}
@Override
public void logDebug(String message) {
System.out.println("[Logback] DEBUG - " + message);
}
@Override
public void logError(String message) {
System.out.println("[Logback] ERROR - " + message);
}
}
/**
* Log4j实现
*/
class Log4jImplementation implements LoggerImplementation {
@Override
public void logInfo(String message) {
System.out.println("[Log4j] INFO - " + message);
}
@Override
public void logDebug(String message) {
System.out.println("[Log4j] DEBUG - " + message);
}
@Override
public void logError(String message) {
System.out.println("[Log4j] ERROR - " + message);
}
}
/**
* 桥接实现:SLF4J Logger
*/
class Slf4jLogger implements Logger {
private LoggerImplementation implementation;
public Slf4jLogger(LoggerImplementation implementation) {
this.implementation = implementation;
}
@Override
public void info(String message) {
implementation.logInfo(message);
}
@Override
public void debug(String message) {
implementation.logDebug(message);
}
@Override
public void error(String message) {
implementation.logError(message);
}
}
/**
* 日志工厂
*/
class LoggerFactory {
private static LoggerImplementation implementation;
static {
// 根据classpath中的实现自动选择
// 简化:这里手动指定
implementation = new LogbackImplementation();
}
public static Logger getLogger(Class<?> clazz) {
return new Slf4jLogger(implementation);
}
public static void setImplementation(LoggerImplementation impl) {
implementation = impl;
}
}
/**
* 测试SLF4J桥接
*/
class Slf4jBridgeDemo {
public static void main(String[] args) {
// 使用Logback实现
System.out.println("=== 使用Logback ===");
Logger logger1 = LoggerFactory.getLogger(Slf4jBridgeDemo.class);
logger1.info("应用启动");
logger1.debug("调试信息");
logger1.error("发生错误");
// 切换到Log4j实现(应用代码不变)
System.out.println("\n=== 切换到Log4j ===");
LoggerFactory.setImplementation(new Log4jImplementation());
Logger logger2 = LoggerFactory.getLogger(Slf4jBridgeDemo.class);
logger2.info("应用启动");
logger2.debug("调试信息");
logger2.error("发生错误");
}
}
5.3 AWT/Swing组件
Java的GUI框架也使用了桥接模式。
java
/**
* AWT/Swing的桥接模式
*
* 抽象部分:Component、Window等
* 实现部分:ComponentPeer(平台相关的实现)
* - WComponentPeer (Windows)
* - XComponentPeer (Linux/X11)
* - LWComponentPeer (轻量级组件)
*/
/**
* 简化的GUI组件桥接实现
*/
// 实现接口:组件对等体
interface ComponentPeer {
void create();
void setVisible(boolean visible);
void setBounds(int x, int y, int width, int height);
}
// Windows平台实现
class WindowsPeer implements ComponentPeer {
@Override
public void create() {
System.out.println("[Windows] 创建Win32窗口");
}
@Override
public void setVisible(boolean visible) {
System.out.println("[Windows] 设置窗口可见性: " + visible);
}
@Override
public void setBounds(int x, int y, int width, int height) {
System.out.println("[Windows] 设置窗口位置和大小: (" + x + ", " + y + ", " + width + ", " + height + ")");
}
}
// Linux平台实现
class LinuxPeer implements ComponentPeer {
@Override
public void create() {
System.out.println("[Linux] 创建X11窗口");
}
@Override
public void setVisible(boolean visible) {
System.out.println("[Linux] 设置窗口可见性: " + visible);
}
@Override
public void setBounds(int x, int y, int width, int height) {
System.out.println("[Linux] 设置窗口位置和大小: (" + x + ", " + y + ", " + width + ", " + height + ")");
}
}
// 抽象组件
abstract class Component {
protected ComponentPeer peer;
public void createPeer() {
peer = createComponentPeer();
peer.create();
}
protected abstract ComponentPeer createComponentPeer();
public void setVisible(boolean visible) {
if (peer != null) {
peer.setVisible(visible);
}
}
public void setBounds(int x, int y, int width, int height) {
if (peer != null) {
peer.setBounds(x, y, width, height);
}
}
}
// 具体组件:窗口
class Window extends Component {
private String os;
public Window(String os) {
this.os = os;
}
@Override
protected ComponentPeer createComponentPeer() {
if ("Windows".equals(os)) {
return new WindowsPeer();
} else if ("Linux".equals(os)) {
return new LinuxPeer();
}
throw new UnsupportedOperationException("不支持的操作系统");
}
}
/**
* 测试GUI桥接
*/
class GuiBridgeDemo {
public static void main(String[] args) {
// Windows平台
System.out.println("=== Windows平台 ===");
Component windowsWindow = new Window("Windows");
windowsWindow.createPeer();
windowsWindow.setBounds(100, 100, 800, 600);
windowsWindow.setVisible(true);
// Linux平台
System.out.println("\n=== Linux平台 ===");
Component linuxWindow = new Window("Linux");
linuxWindow.createPeer();
linuxWindow.setBounds(50, 50, 1024, 768);
linuxWindow.setVisible(true);
}
}
六、桥接模式的优缺点
6.1 优点
1. 分离抽象和实现
抽象和实现可以独立变化
不会相互影响
2. 减少子类数量
不使用桥接:
形状类 = 形状数 × 颜色数
3种形状 × 3种颜色 = 9个类
使用桥接:
形状类 + 颜色类 = 形状数 + 颜色数
3种形状 + 3种颜色 = 6个类
减少了类的数量!
3. 提高可扩展性
添加新的抽象:不影响实现
添加新的实现:不影响抽象
符合开闭原则
4. 隐藏实现细节
客户端只知道抽象接口
不需要了解具体实现
5. 运行时切换实现
java
// 可以在运行时动态切换实现
Shape shape = new Circle(new Red(), 5.0);
// 后续可以改为蓝色
// shape.setColor(new Blue());
6.2 缺点
1. 增加系统复杂度
需要理解抽象和实现的分离
引入更多的类和接口
2. 设计难度增加
需要正确识别系统中的两个独立变化维度
需要合理划分抽象和实现
3. 不适合简单系统
如果只有一个维度变化
使用桥接模式反而增加复杂度
七、最佳实践
7.1 识别独立变化的维度
java
/**
* 判断是否需要桥接模式的标准:
* 1. 是否有两个或多个独立变化的维度?
* 2. 这些维度是否需要独立扩展?
* 3. 这些维度的组合是否会导致类爆炸?
*/
// 示例:报表系统
// 维度1:报表类型(销售报表、库存报表、财务报表)
// 维度2:输出格式(PDF、Excel、HTML)
// 实现接口:报表格式
interface ReportFormatter {
void format(String data);
}
class PdfFormatter implements ReportFormatter {
@Override
public void format(String data) {
System.out.println("生成PDF格式: " + data);
}
}
class ExcelFormatter implements ReportFormatter {
@Override
public void format(String data) {
System.out.println("生成Excel格式: " + data);
}
}
// 抽象报表
abstract class Report {
protected ReportFormatter formatter;
public Report(ReportFormatter formatter) {
this.formatter = formatter;
}
public abstract void generate();
}
class SalesReport extends Report {
public SalesReport(ReportFormatter formatter) {
super(formatter);
}
@Override
public void generate() {
String data = "销售数据...";
formatter.format(data);
}
}
7.2 合理设计抽象层次
java
/**
* 抽象层次设计原则:
* 1. 抽象部分定义业务逻辑
* 2. 实现部分定义平台/技术细节
* 3. 通过接口连接两者
*/
// 好的设计
abstract class PaymentMethod { // 抽象:支付方式
protected PaymentGateway gateway; // 实现:支付网关
public void pay(double amount) {
// 业务逻辑
gateway.processPayment(amount);
}
}
// 不好的设计:混合业务和技术细节
class AlipayPayment { // 既包含支付方式,又包含具体实现
// 难以扩展和维护
}
7.3 使用依赖注入
java
/**
* 通过依赖注入设置实现
*/
public class MessageWithDI extends Message {
public MessageWithDI(MessageSender sender) {
super(sender);
}
// 支持运行时切换实现
public void setSender(MessageSender sender) {
this.sender = sender;
}
@Override
protected String formatContent() {
return "消息内容";
}
}
// 使用
/*
MessageWithDI message = new MessageWithDI(new EmailSender());
message.sendMessage("user@example.com");
// 切换发送方式
message.setSender(new SmsSender());
message.sendMessage("138****8888");
*/
7.4 结合工厂模式
java
/**
* 使用工厂创建桥接对象
*/
class ShapeFactory {
public static Shape createShape(String shapeType, String colorType) {
Color color = ColorFactory.createColor(colorType);
switch (shapeType) {
case "circle":
return new Circle(color, 5.0);
case "rectangle":
return new Rectangle(color, 10.0, 5.0);
default:
throw new IllegalArgumentException("未知形状类型");
}
}
}
class ColorFactory {
public static Color createColor(String colorType) {
switch (colorType) {
case "red":
return new Red();
case "blue":
return new Blue();
case "green":
return new Green();
default:
throw new IllegalArgumentException("未知颜色类型");
}
}
}
// 使用
// Shape shape = ShapeFactory.createShape("circle", "red");
八、总结
8.1 核心要点
- 桥接模式的本质:将抽象部分与实现部分分离,使它们可以独立变化
- 两个关键角色 :
- 抽象部分:定义高层业务逻辑
- 实现部分:定义底层技术实现
- 适用场景 :
- 存在两个独立变化的维度
- 需要避免类的爆炸式增长
- 需要运行时切换实现
- 核心优势 :
- 分离关注点
- 提高可扩展性
- 符合开闭原则
8.2 使用建议
选择桥接模式的检查清单:
✓ 是否有两个或多个独立变化的维度?
✓ 使用继承是否导致类数量爆炸?
✓ 是否需要在运行时切换实现?
✓ 抽象和实现是否需要独立扩展?
如果有2个以上"是",建议使用桥接模式!
8.3 与其他模式的对比
桥接 vs 适配器:
- 桥接:设计时分离抽象和实现
- 适配器:事后适配不兼容的接口
桥接 vs 策略:
- 桥接:两个维度独立变化
- 策略:一个维度的算法替换
桥接 vs 抽象工厂:
- 桥接:分离抽象和实现
- 抽象工厂:创建相关对象家族
8.4 实践经验
- 识别独立维度:找出系统中独立变化的维度
- 合理划分职责:抽象负责业务,实现负责技术
- 使用组合而非继承:通过组合连接抽象和实现
- 保持接口简单:实现接口应该简洁明了
- 结合其他模式:可以与工厂、策略等模式结合使用