什么是桥接模式


桥接模式(Bridge Pattern)也称之为桥梁模式,接口(Interface)模式或者柄体(Handle and Body)模式,其核心思想是将抽象部分与它的实现部分分离,使抽象部分和实现部分两个维度都可以独立的变化。
桥接模式属于结构型模式,主要目的是通过组合的方式建立两个类之间的关系,而不是通过继承来实现,从而达到解耦抽象和实现的目的。

示例

在 Web 开发中,我们一般都有三种常见的发送消息的方式:站内信、短信(SMS)、邮件。而假如系统中的消息又需要按照紧急程度来划分普通消息和加急消息,那么这就有了两个维度:

  1. 抽象部分维度:发送消息的类型。
  2. 实现部分维度:普通消息和紧急消息。

下面就让我们以发送消息的这两个维度来实现一个桥接模式(这里我们需要新建一个 bridge 目录,相关类创建在 bridge 目录下)。

  • 首先新建一个子系统 IMessage.java,这里面只定义一个发送消息的方法。 ```java package bridge;

public interface IMessage { void send(String content,String toUser);//发送消息 }

  1. package bridge; public interface IMessage { void send(String content,String toUser);//发送消息 }
  2. - 接下来需要建立三种消息类型之短信消息类 SmsMessage.java 并实现 IMessage 接口。
  3. ```java
  4. package bridge;
  5. public class SmsMessage implements IMessage {
  6. @Override
  7. public void send(String content, String toUser) {
  8. System.out.println(String.format("SMS消息->%s:%s",toUser,content));
  9. }
  10. }

package bridge; public class SmsMessage implements IMessage { @Override public void send(String content, String toUser) { System.out.println(String.format(“SMS消息->%s:%s”,toUser,content)); } }

  • 继续建立三种消息类型之邮件消息类 EmailMessage.java 并实现 IMessage 接口。 ```java package bridge;

public class EmailMessage implements IMessage { @Override public void send(String content, String toUser) { System.out.println(String.format(“邮件消息->%s:%s”,toUser,content)); } }

  1. package bridge; public class EmailMessage implements IMessage { @Override public void send(String content, String toUser) { System.out.println(String.format("邮件消息->%s:%s",toUser,content)); } }
  2. - 继续建立三种消息类型之站内消息类 WebMessage.java 并实现 IMessage 接口。
  3. ```java
  4. package bridge;
  5. public class WebMessage implements IMessage {
  6. @Override
  7. public void send(String content, String toUser) {
  8. System.out.println(String.format("站内消息->%s:%s",toUser,content));
  9. }
  10. }

package bridge; public class WebMessage implements IMessage { @Override public void send(String content, String toUser) { System.out.println(String.format(“站内消息->%s:%s”,toUser,content)); } }

  • 现在我们需要建立一个中介人即桥接者类 AbstractBridgeMessage.java,桥接者类一般设置为抽象类,其内部需要持有抽象维护(消息类型)的引用,并且定义和抽象维度类相同功能的方法。
  1. package bridge;
  2. public abstract class AbstractBridgeMessage {
  3. private IMessage iMessage;//持有抽象维度的引用
  4. public AbstractBridgeMessage(IMessage iMessage) {
  5. this.iMessage = iMessage;
  6. }
  7. public void sendMessage(String content,String toUser){//定义一个和抽象维度类中具有相同功能的方法:发送消息
  8. this.iMessage.send(content,toUser);//调用抽象维度内方法:发送消息
  9. }
  10. }

package bridge; public abstract class AbstractBridgeMessage { private IMessage iMessage;//持有抽象维度的引用 public AbstractBridgeMessage(IMessage iMessage) { this.iMessage = iMessage; } public void sendMessage(String content,String toUser){//定义一个和抽象维度类中具有相同功能的方法:发送消息 this.iMessage.send(content,toUser);//调用抽象维度内方法:发送消息 } }

  • 接下来需要建立两个用于表示不同紧急程度的对象之普通消息 CommonMsg.java,并继承桥接者类 AbstractBridgeMessage,这就是桥接模式的核心之处,因为普通消息这个具体维度不去继承抽象维度,而是继承桥接者,这就实现了抽象和实现的解耦,桥接者在中间起到了桥梁的作用。
  1. package bridge;
  2. public class CommonMsg extends AbstractBridgeMessage {
  3. public CommonMsg(IMessage iMessage) {
  4. super(iMessage);
  5. }
  6. @Override
  7. public void sendMessage(String content, String toUser) {
  8. this.doSomething();//做一点普通消息该做的事
  9. super.sendMessage(content, toUser);//调用桥接者中的方法:发送消息
  10. }
  11. private void doSomething() {
  12. System.out.println("我只是一个普通消息,什么都不用做");
  13. }
  14. }

package bridge; public class CommonMsg extends AbstractBridgeMessage { public CommonMsg(IMessage iMessage) { super(iMessage); } @Override public void sendMessage(String content, String toUser) { this.doSomething();//做一点普通消息该做的事 super.sendMessage(content, toUser);//调用桥接者中的方法:发送消息 } private void doSomething() { System.out.println(“我只是一个普通消息,什么都不用做”); } }

  • 最后再建立一个紧急消息类 UrgentMessage.java。
  1. package bridge;
  2. public class UrgentMessage extends AbstractBridgeMessage {
  3. public UrgentMessage(IMessage iMessage) {
  4. super(iMessage);
  5. }
  6. @Override
  7. public void sendMessage(String content, String toUser) {
  8. doSomething();//做点紧急消息需要做的事情
  9. super.sendMessage(content, toUser);//调用桥接者中的方法:发送消息
  10. }
  11. private void doSomething() {
  12. System.out.println("这是紧急消息,请优先发送");
  13. }
  14. }

package bridge; public class UrgentMessage extends AbstractBridgeMessage { public UrgentMessage(IMessage iMessage) { super(iMessage); } @Override public void sendMessage(String content, String toUser) { doSomething();//做点紧急消息需要做的事情 super.sendMessage(content, toUser);//调用桥接者中的方法:发送消息 } private void doSomething() { System.out.println(“这是紧急消息,请优先发送”); } }

  • 现在可以建立一个测试类 TestBridge.java 来测试一下了。
  1. package bridge;
  2. import java.io.IOException;
  3. public class TestBridge {
  4. public static void main(String[] args) throws IOException {
  5. IMessage iMessage = new EmailMessage();
  6. AbstractBridgeMessage abstractBridgeMessage = new UrgentMessage(iMessage);//紧急邮件消息
  7. abstractBridgeMessage.sendMessage("您好","双子孤狼1号");
  8. //再来一个普通短信消息
  9. System.out.println("------------分割线---------------");
  10. iMessage = new SmsMessage();
  11. abstractBridgeMessage = new CommonMsg(iMessage);
  12. abstractBridgeMessage.sendMessage("您好","双子孤狼2号");
  13. //最后再来一个紧急的站内信
  14. System.out.println("------------分割线---------------");
  15. iMessage = new WebMessage();
  16. abstractBridgeMessage = new UrgentMessage(iMessage);
  17. abstractBridgeMessage.sendMessage("您好","实验楼的小伙伴");
  18. }
  19. }

package bridge; import java.io.IOException; public class TestBridge { public static void main(String[] args) throws IOException { IMessage iMessage = new EmailMessage(); AbstractBridgeMessage abstractBridgeMessage = new UrgentMessage(iMessage);//紧急邮件消息 abstractBridgeMessage.sendMessage(“您好”,”双子孤狼1号”); //再来一个普通短信消息 System.out.println(“——————分割线———————-“); iMessage = new SmsMessage(); abstractBridgeMessage = new CommonMsg(iMessage); abstractBridgeMessage.sendMessage(“您好”,”双子孤狼2号”); //最后再来一个紧急的站内信 System.out.println(“——————分割线———————-“); iMessage = new WebMessage(); abstractBridgeMessage = new UrgentMessage(iMessage); abstractBridgeMessage.sendMessage(“您好”,”实验楼的小伙伴”); } }
执行 javac bridge/*.java 命令进行编译,然后再执行 java bridge.TestBridge 命令运行测试类(大家一定要自己动手运行哦,只有自己实际去运行了才会更能体会其中的思想)。
桥接模式 - 图1
这就是一个桥接模式,后面这两个维度无论哪个维度需要扩展,都只需要新建一个类就好了,扩展起来非常的方便。

桥接模式适用场景

  1. 在抽象和具体之间需要增加更多灵活性的场景。
  2. 一个类存在 2 个或者以上独立变化的维度,而这些维度又需要独立进行扩展时,但是需要注意的是桥接模式一般用于抽象和实现解耦,多个维度一般是指的一个抽象维度和多个具体维度。
  3. 不希望使用继承,或因为多层继承导致类的个数剧增时可以考虑使用桥接模式。

    桥接模式优点

  4. 分离了抽象部分及其实现部分两个维度,实现了代码的解耦,提高了系统的扩展性。

  5. 扩展功能时只需要新增类,无需修改源代码,符合开闭原则。
  6. 通过组合而不是继承来实现耦合,符合合成复用原则。

    桥接模式缺点

  7. 增加了系统的理解难度和设计难度(和类的膨胀缺点一样,这也算是大部分设计模式的共性)。

  8. 需要正确识别系统中各个独立变化的维度。