← 返回题目列表

如何手写一个桥接模式?Java 代码怎么实现?

高频 中等 第 11 / 25 题 更新于 2026/07/28
桥接模式Java代码实现

简化版

手写桥接模式的关键是先识别两个变化维度,再把其中一个维度抽成接口,另一个维度作为抽象类持有这个接口。Java 实现中通常是“抽象业务类 + 实现接口 + 多个业务子类 + 多个实现类”。

详细版

以“通知消息 × 发送渠道”为例:

  • 消息类型是抽象维度:普通通知、紧急通知。
  • 发送渠道是实现维度:短信、邮件。
  • Notification 持有 Sender
  • UrgentNotification 可以增强发送内容。
  • SmsSenderEmailSender 只负责具体发送。

这种写法避免创建 UrgentSmsNotificationUrgentEmailNotificationNormalSmsNotification 等组合类。

完整版教学

一、先定义实现接口

实现接口代表底层能力。这里是发送渠道:

interface Sender {
    void send(String target, String content);
}

这个接口要尽量贴近底层能力,不要把太多业务语义塞进去。比如不要设计成 sendUrgentOrderMessage(),否则实现层又会被业务类型污染。

二、编写具体实现类

class SmsSender implements Sender {
    @Override
    public void send(String target, String content) {
        System.out.println("SMS -> " + target + ": " + content);
    }
}

class EmailSender implements Sender {
    @Override
    public void send(String target, String content) {
        System.out.println("Email -> " + target + ": " + content);
    }
}

具体实现类只关注如何把内容发出去。短信有短信的网关,邮件有邮件的 SMTP 或邮件服务商,这些细节不应该进入通知类型类。

三、定义抽象部分

abstract class Notification {
    protected final Sender sender;

    protected Notification(Sender sender) {
        this.sender = sender;
    }

    public abstract void notify(String target, String content);
}

Notification 就是桥接模式里的 Abstraction。它持有 Sender,但不依赖 SmsSenderEmailSender

四、编写扩展抽象类

class NormalNotification extends Notification {
    public NormalNotification(Sender sender) {
        super(sender);
    }

    @Override
    public void notify(String target, String content) {
        sender.send(target, content);
    }
}

class UrgentNotification extends Notification {
    public UrgentNotification(Sender sender) {
        super(sender);
    }

    @Override
    public void notify(String target, String content) {
        sender.send(target, "[紧急] " + content);
    }
}

紧急通知的业务规则留在 UrgentNotification 中,发送渠道仍然只处理发送。

五、客户端组合使用

public class BridgeDemo {
    public static void main(String[] args) {
        Sender sms = new SmsSender();
        Sender email = new EmailSender();

        Notification urgentSms = new UrgentNotification(sms);
        urgentSms.notify("13800000000", "订单支付失败");

        Notification normalEmail = new NormalNotification(email);
        normalEmail.notify("user@example.com", "欢迎注册");
    }
}

这里客户端可以自由组合:

  • 普通通知 + 短信
  • 普通通知 + 邮件
  • 紧急通知 + 短信
  • 紧急通知 + 邮件

但代码上只需要 2 个通知类和 2 个发送类,而不是 4 个组合类。

六、新增能力时如何扩展

如果新增企业微信发送,只加:

class WeComSender implements Sender {
    @Override
    public void send(String target, String content) {
        System.out.println("WeCom -> " + target + ": " + content);
    }
}

如果新增验证码通知,只加:

class VerifyCodeNotification extends Notification {
    public VerifyCodeNotification(Sender sender) {
        super(sender);
    }

    @Override
    public void notify(String target, String content) {
        sender.send(target, "验证码:" + content + ",5分钟内有效");
    }
}

两个方向互不修改,这就是桥接模式带来的扩展性。

七、实现时的注意点

桥接模式实现并不复杂,难点在于边界划分。

  • 抽象类不要直接 new 具体实现类,否则仍然耦合。
  • 实现接口不要暴露过多业务语义,否则维度会混在一起。
  • 具体实现类不要依赖具体抽象类,否则桥会变成环。
  • 如果对象创建逻辑复杂,可以配合工厂或依赖注入容器管理。

八、常见误区与追问

手写实现要验证组合关系和扩展路径,而不只是类图完整。假设消息有普通、加急2种,渠道有邮件、短信2种,桥接后只需4个具体类;增加微信渠道只新增1个 Implementor,不改两种消息类。构造时注入接口还能让单元测试替换底层发送器。

检查维度判定依据
抽象侧扩展新增 RefinedAbstraction
实现侧扩展新增 Implementor 实现
UrgentMessage --组合--> MessageSender <--实现-- SmsSender

易错点:桥接的关键代码是“持有接口并委托”,不是两组类碰巧同时存在。

  • 误区:桥接实现必须使用继承。 抽象侧与实现侧的连接依赖组合,双方内部是否继承并非核心。
  • 追问:Implementor 应由谁选择? 工厂、依赖注入容器或客户端装配代码都可以根据配置选择。
  • 误区:每个 RefinedAbstraction 都要绑定固定实现。 运行期可注入不同 Implementor,这正是组合带来的灵活性。
  • 追问:如何测试委托是否正确? 注入假 Implementor,断言高层方法传入了正确参数和调用次数。
  • 追问:实现类增多怎么管理? 按两个维度分别组织包和注册表,避免重新形成笛卡尔积命名。

九、加强记忆

手写桥接模式时按“先拆维度,再抽接口,最后组合”的顺序:一个维度做抽象业务类,另一个维度做实现接口,抽象对象持有实现接口,客户端负责把两边拼起来。