桥接模式

1 实际场景

1.1 发送提示信息

考虑一个发送提示消息的实际业务功能。基本所有带业务流程处理的系统都会有这样的功能。

从业务上来看,消息可以分为普通消息、加急消息、特急消息多种。不同的消息类型,业务功能处理逻辑不一样。如:加急消息会在消息上添加加急;特急消息处理在消息上添加特急标识之外,还在会另外发送一条催促的消息。其中发送消息的方式可以分为系统消息,手机消息,邮件等。

如何实现?

1.2 非设计模式的解决方案

1.2.1 简化版本

先实现一个简化的功能:消息的类型只有普通类型,发送的方式只有站内消息邮件。将问题简单化,方便后续说明。

  1. 由于发送普通消息会有两种不同的实现方式,为了让外部能统一操作,因此,把消息设计成接口,然后由两个不同的实现类,分别实现系统内短消息方式和邮件发送消息的方式。此时系统结构如图所示:

    image-20240102131358457
  2. 消息的统一接口代码如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /**
    * @author mark.hct
    * @version Message.java v 0.1 2023/03/27 23:57 Exp $
    * @description 消息的统一接口
    */
    public interface Message {
    /**
    * 发送消息
    *
    * @param message 要发送的消息内容
    * @param toUser 消息发送的目的人员
    */
    public void send(String message, String toUser);
    }
  3. 两种消息的实现类型代码如下:(这里只是做示意,简单实现消息的类型)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    /**
    * @author mark.hct
    * @version CommonMessageSMS.java v 0.1 2023/03/27 23:57 Exp $
    * @description 以站内短消息的方式发送普通消息
    */
    public class CommonMessageSMS implements Message {
    @Override
    public void send(String message, String toUser) {
    System.out.println("使用站内短消息的方式,发送消息'" + message + "'给" + toUser);
    }
    }

    /**
    * @author mark.hct
    * @version CommonMessageEmail.java v 0.1 2023/03/27 23:58 Exp $
    * @description 以Email的方式发送普通消息
    */
    public class CommonMessageEmail implements Message {
    @Override
    public void send(String message, String toUser) {
    System.out.println("使用Email的方式,发送消息'" + message + "'给" + toUser);
    }
    }

1.2.2 增加消息类型

上述简单版本的示例看着非常简单,若是还需要新增消息类型:加急信息,发送方式也是站内消息邮件。其中与普通消息不同的是,加急消息会在消息内容中添加加急的前缀,然后在发送消息;另外加急消息会提供监控的方法,用户可以通过这个方法来查询加急消息的处理进度。因此加急消息需要扩展一个新的接口,处理发送消息的基本方法之外,还需要提供监控的功能,系统结构图如下:

image-20240102132442529
  1. 需要扩展的加急消息的接口(监控功能)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /**
    * @author mark.hct
    * @version UrgencyMessage.java v 0.1 2023/03/28 00:02 Exp $
    * @description 加急消息的抽象接口
    */
    public interface UrgencyMessage extends Message {
    /**
    * 监控某消息的处理过程
    *
    * @param messageId 被监控的消息的编号
    * @return 包含监控到的数据对象,这里示意一下,所以用了Object
    */
    Object watch(String messageId);
    }
  2. 对应的两种消息发送方式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    /**
    * @author mark.hct
    * @version UrgencyMessageSMS.java v 0.1 2023/03/28 00:03 Exp $
    * @description
    */
    public class UrgencyMessageSMS implements UrgencyMessage {
    @Override
    public void send(String message, String toUser) {
    message = "加急:" + message;
    System.out.println("使用站内短消息的方式,发送消息'" + message + "'给" + toUser);
    }

    @Override
    public Object watch(String messageId) {
    //获取相应的数据,组织成监控的数据对象,然后返回
    return null;
    }
    }


    /**
    * @author mark.hct
    * @version UrgencyMessageEmail.java v 0.1 2023/03/28 00:04 Exp $
    * @description
    */
    public class UrgencyMessageEmail implements UrgencyMessage {
    @Override
    public void send(String message, String toUser) {
    message = "加急:" + message;
    System.out.println("使用Email的方式,发送消息'" + message + "'给" + toUser);
    }

    @Override
    public Object watch(String messageId) {
    //获取相应的数据,组织成监控的数据对象,然后返回
    return null;
    }
    }
    1. 在实现加急消息的实际过程中,一般是让加急消息处理的对象继承普通消息的实现,来满足发送不一样消息内容的需求,这里为了示例结构简单,没有这样做。

1.2.2 有何问题

上述做法中,也能满足需求。假设需要持续添加消息类型:特级消息,特级消息是在不同消息的处理基础上,添加催促功能,并且投递消息的方式还是站内消息邮件,系统结构图如下:

image-20240102205620274

通过系统结构图可以看出,通过继承的方式来扩展,会非常不方便。每次添加新的消息类型时,都需要将所有的消息投递方式都实现一遍(站内消息邮件)。

若是新增投递方式,则需要对当前所有的消息类型都适配,如下系统结构图,新增了手机的消息投递方式,则需要对每个消息类型的具体实现中添加新的处理逻辑。

image-20240102210024290

通过上述的例子可以直观地看出,扩展消息类型相当麻烦,不同的消息具有不同的实现。对于每一类的消息都有不同的发送消息的方式。而且如果要新增消息发送方式的时候,那么就要求所有的消息类型,都实现适配新的发送方式

2 桥接模式

2.1 定义

适配器模式(Bridge Pattern)属于结构型模式,将抽象部分与它的实现部分分离,使它们都可以独立地变化

2.2 桥接模式解决问题思路

分析示例,可以消息拆分为两个维度:消息类型发送消息的方式,其中消息类型有,普通消息、加急信息、特殊加急信息;发送消息方式有,站内信息、手机短信、Email。关系图如下:

image-20240102230839837

前面分析扩展困难的原因是,消息的抽象和实现是混合在一起的,这就导致其中一个维度变化的话,则会引起另外一个维度的变化。

解决的方法就是将两个维度拆分开来,相互独立,这样可以实现独立的变化,是扩展变得简单。

桥接模式通过引入实现的接口,将实现部分从系统中分离出去,将顶层的抽象接口改成抽象类,在类里面持有一个具体的实现部分的实例,抽象部分与实现部分结合起来

这样一来,对于需要发送消息的客户端而言,就只需要创建相应的消息对象,然后调用这个消息对象的方法就可以了,这个消息对象会调用持有的真正的消息发送方式来把消息发送出去。也就是说客户端只是想要发送消息而已,并不想关心具体如何发送。

2.3 结构说明

image-20240102234024753
  • Abstraction :抽象部分的接口,通常在这个对象里面维护一个实现部分的对象引用,在抽象对象的方法中调用实现部分的完成,在这个方法中通常都是跟具体的业务相关的方法;
  • RefinedAbstract:扩展抽象部分的接口,通常在这些对象里面,定义与实际业务相关的方法,这些方法中会使用 Abstraction 中定义的方法,也可能需要调用实现部分的对象来完成;
  • Implementor:定义实现部分的接口,这个接口不用和 Abstraction 里面的方法一致,通常是由 Implementor 接口提供基本的操作,而 Abstraction 里面定义的是基于这些基本操作的业务方法,也就是说 Abstraction 定义了基于这些基本操作的较高层次的操作;
  • ConcreteImplementor:实现 Implementor 接口的对象;

2.4 示例代码

  1. Implementor 接口定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**
    * @author mark.hct
    * @version Implementor.java v 0.1 2023/03/28 23:34 Exp $
    * @description 定义实现部分的接口,可以与抽象部分接口的方法不一样
    */
    public interface Implementor {
    /**
    * 示例方法,实现抽象部分需要的某些具体功能
    */
    void operationImpl();
    }
  2. Abstraction 抽象接口定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    /**
    * @author mark.hct
    * @version Abstraction.java v 0.1 2023/03/28 23:34 Exp $
    * @description 定义抽象部分的接口
    */
    public abstract class Abstraction {
    /**
    * 持有一个实现部分的对象
    */
    protected Implementor impl;

    /**
    * 构造方法,传入实现部分的对象
    *
    * @param impl 实现部分的对象
    */
    public Abstraction(Implementor impl) {
    this.impl = impl;
    }

    /**
    * 示例操作,实现一定的功能,可能需要转调实现部分的具体实现方法
    */
    public void operation() {
    impl.operationImpl();
    }
    }
  3. 具体的实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    /**
    * @author mark.hct
    * @version ConcreteImplementorA.java v 0.1 2023/03/28 23:37 Exp $
    * @description 真正的具体实现对象
    */
    public class ConcreteImplementorA implements Implementor {

    @Override
    public void operationImpl() {
    //真正的实现
    }
    }


    /**
    * @author mark.hct
    * @version ConcreteImplementorB.java v 0.1 2023/03/28 23:38 Exp $
    * @description 真正的具体实现对象
    */
    public class ConcreteImplementorB implements Implementor {
    @Override
    public void operationImpl() {
    //真正的实现
    }
    }
  4. 扩展 Abstraction 接口的对象实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    /**
    * @author mark.hct
    * @version RefinedAbstraction.java v 0.1 2023/03/28 23:39 Exp $
    * @description 扩充由Abstraction定义的接口功能
    */
    public class RefinedAbstraction extends Abstraction {

    public RefinedAbstraction(Implementor impl) {
    super(impl);
    }

    /**
    * 示例操作,实现一定的功能
    */
    public void otherOperation() {
    //实现一定的功能,可能会使用具体实现部分的实现方法,
    //但是本方法更大的可能是使用Abstraction中定义的方法,
    //通过组合使用Abstraction中定义的方法来完成更多的功能
    }
    }

2.5 重写方案

首先将消息的抽象和实现分离出来,分析要实现的功能,抽象部分就是消息类型对应的功能,而实现部分是发送消息的方式。

2.5.1 简化版本

桥接模式的结构图:

image-20240103131622622
  1. 实现部分 - 定义的接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    /**
    * @author mark.hct
    * @version MessageImplementor.java v 0.1 2024/3/29 13:17 Exp $
    * @description 实现发送消息的统一接口
    */
    public interface MessageImplementor {
    /**
    * 发送消息
    *
    * @param message 要发送的消息内容
    * @param toUser 消息发送的目的人员
    */
    void send(String message, String toUser);
    }
  2. 抽象部分 - 定义的接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    /**
    * @author mark.hct
    * @version AbstractMessage.java v 0.1 2024/3/29 13:17 Exp $
    * @description 抽象的消息对象
    */
    public abstract class AbstractMessage {
    /**
    * 持有一个实现部分的对象
    */
    protected MessageImplementor impl;

    /**
    * 构造方法,传入实现部分的对象
    *
    * @param impl 实现部分的对象
    */
    public AbstractMessage(MessageImplementor impl) {
    this.impl = impl;
    }

    /**
    * 发送消息,转调实现部分的方法
    *
    * @param message 要发送的消息内容
    * @param toUser 消息发送的目的人员
    */
    public void sendMessage(String message, String toUser) {
    this.impl.send(message, toUser);
    }
    }
  3. 具体的实现,即消息的投递方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    /**
    * @author mark.hct
    * @version CommonMessage.java v 0.1 2024/3/29 13:27 Exp $
    * @description
    */
    public class CommonMessage extends AbstractMessage {
    public CommonMessage(MessageImplementor impl) {
    super(impl);
    }

    @Override
    public void sendMessage(String message, String toUser) {
    // 对于普通消息,什么都不干,直接调父类的方法,把消息发送出去就可以了
    super.sendMessage(message, toUser);
    }
    }



    /**
    * @author mark.hct
    * @version UrgencyMessage.java v 0.1 2024/3/29 13:27 Exp $
    * @description
    */
    public class UrgencyMessage extends AbstractMessage {
    public UrgencyMessage(MessageImplementor impl) {
    super(impl);
    }

    @Override
    public void sendMessage(String message, String toUser) {
    super.sendMessage(message, toUser);
    }

    /**
    * 扩展自己的新功能:监控某消息的处理过程
    *
    * @param messageId 被监控的消息的编号
    * @return 包含监控到的数据对象,这里示意一下,所以用了Object
    */
    public Object watch(String messageId) {
    //获取相应的数据,组织成监控的数据对象,然后返回
    return null;
    }
    }
  4. 抽象部分的拓展,即消息的类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    /**
    * @author mark.hct
    * @version CommonMessage.java v 0.1 2024/3/29 13:27 Exp $
    * @description
    */
    public class CommonMessage extends AbstractMessage {
    public CommonMessage(MessageImplementor impl) {
    super(impl);
    }

    @Override
    public void sendMessage(String message, String toUser) {
    // 对于普通消息,什么都不干,直接调父类的方法,把消息发送出去就可以了
    super.sendMessage(message, toUser);
    }
    }


    /**
    * @author mark.hct
    * @version UrgencyMessage.java v 0.1 2024/3/29 13:27 Exp $
    * @description
    */
    public class UrgencyMessage extends AbstractMessage {
    public UrgencyMessage(MessageImplementor impl) {
    super(impl);
    }

    @Override
    public void sendMessage(String message, String toUser) {
    super.sendMessage(message, toUser);
    }

    /**
    * 扩展自己的新功能:监控某消息的处理过程
    *
    * @param messageId 被监控的消息的编号
    * @return 包含监控到的数据对象,这里示意一下,所以用了Object
    */
    public Object watch(String messageId) {
    //获取相应的数据,组织成监控的数据对象,然后返回
    return null;
    }
    }

2.5.2 添加功能

在抽象部分(消息类型)添加一个特级消息,实现部分(消息投递方式)添加一个收集发送消息的方式。

  1. 特急消息的处理类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /**
    * @author mark.hct
    * @version SpecialUrgencyMessage.java v 0.1 2024/3/29 13:30 Exp $
    * @description
    */
    public class SpecialUrgencyMessage extends AbstractMessage {

    public SpecialUrgencyMessage(MessageImplementor impl) {
    super(impl);
    }

    @Override
    public void sendMessage(String message, String toUser) {
    //还需要增加一条待催促的信息
    message = "特急:"+message;
    super.sendMessage(message, toUser);
    }

    public void hurry(String messageId) {
    //执行催促的业务,发出催促的信息
    }
    }
  2. 手机消息投递方式的实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /**
    * @author mark.hct
    * @version MessageMobile.java v 0.1 2024/3/29 13:31 Exp $
    * @description 以手机短消息的方式发送消息
    */
    public class MessageMobile implements MessageImplementor {
    @Override
    public void send(String message, String toUser) {
    System.out.println("使用手机短消息的方式,发送消息'"+message+"'给"+toUser);
    }
    }

2.5.3 功能测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* @author mark.hct
* @version Client.java v 0.1 2024/3/29 13:32 Exp $
* @description
*/
public class Client {
public static void main(String[] args) {
//创建具体的实现对象
MessageImplementor impl = new MessageSMS();
//创建一个普通消息对象
AbstractMessage m = new CommonMessage(impl);
m.sendMessage("请喝一杯茶", "小马");
//创建一个紧急消息对象
m = new UrgencyMessage(impl);
m.sendMessage("请喝一杯茶", "小马");
//创建一个特急消息对象
m = new SpecialUrgencyMessage(impl);
m.sendMessage("请喝一杯茶", "小马");

//把实现方式切换成手机短消息,然后再实现一遍
impl = new MessageMobile();
m = new CommonMessage(impl);
m.sendMessage("请喝一杯茶", "小马");
m = new UrgencyMessage(impl);
m.sendMessage("请喝一杯茶", "小马");
m = new SpecialUrgencyMessage(impl);
m.sendMessage("请喝一杯茶", "小马");
}
}

执行结果:

1
2
3
4
5
6
使用站内短消息的方式,发送消息'请喝一杯茶'给小马
使用站内短消息的方式,发送消息'请喝一杯茶'给小马
使用站内短消息的方式,发送消息'特急:请喝一杯茶'给小马
使用手机短消息的方式,发送消息'请喝一杯茶'给小马
使用手机短消息的方式,发送消息'请喝一杯茶'给小马
使用手机短消息的方式,发送消息'特急:请喝一杯茶'给小马

3 模式讲解

所谓桥接,就是把分离的抽象部分和实现部分进行连接(搭桥)

在桥接模式中的桥接是单向的,只能是抽象部分对象去使用具体实现部分的对象,不能反过来

只要让抽象部分拥有实现部分的接口对象,就属于桥接关系,再抽象部分中,就可以通过这个接口来调用具体的实现部分的功能,维护桥接,也就维护了这个关系。

桥接模式的意图:是的抽象和实现可以独立变化,可以分别扩充,两者之间是非常松散的关系,某种角度来讲,抽象和实现部分可以完全分开独立,抽象部分不过是一个使用实现部分对外接口的程序。类似于策略模式,抽象部分需要根据某个策略,来选择真实的实现,也就是说桥接模式的抽象部分相当于策略模式的上下文。更原始的就直接类似于面向接口编程,通过接口分离的两个部分而已。但是,桥接模式的抽象部分,是可以继续扩展和变化,而策略模式只有上下文,是不存在所谓抽象部分

那抽象和实现为何还要组合在一起呢?原因是在抽象部分和实现部分还是存在内部联系,抽象部分的实现通常是需要调用实现部分的功能来实现

3.1 退化的桥接模式

如果 Implementor 仅有一个实现,那么就没有必要创建 Implementor 接口了,这是一种桥接模式退化的情况。这个时候 AbstractionImplementor 是一对一的关系,虽然如此,也还是要保持它们的分离状态,这样的话,它们才不会相互影响,才可以分别扩展。

3.2 桥接实现分析

如何拆分需求中的抽象部分和实现部分,如上述例子中,谁来负责创建 Implementor 的对象,谁来抽象部分,有以下几种方式:

1、由抽象部分的对象自己来创建相应的 Implementor 的对象

a. 从外面传递参数比较简单,如上述示例,如果用一个 type 来标识具体采用哪种发送消息的方案,然后在 Abstraction 的构造方法中,根据 type 进行创建;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/** 
* 抽象的消息对象
*/
public abstract class AbstractMessage {
/**
* 持有一个实现部分的对象
*/
protected MessageImplementor impl;
/**
* 构造方法,传入选择实现部分的类型
* @param type 传入选择实现部分的类型
*/
public AbstractMessage(int type){
if(type==1){
this.impl = new MessageSMS();
}else if(type==2){
this.impl = new MessageEmail();
}else if(type==3){
this.impl = new MessageMobile();
}
}
/**
* 发送消息,转调实现部分的方法
* @param message 要发送的消息内容
* @param toUser 把消息发送的目的人员
*/
public void sendMessage(String message,String toUser){
this.impl.send(message, toUser);
}
}

b. 对于不需要外部传入参数的情况,那就说明是在 Abstraction 的实现中,根据具体的参数数据来选择相应的 Implementor 对象。有可能在 Abstraction 的构造方法中选,也有可能在具体的方法中选择;

比如前面的示例,如果发送的消息长度在 100 以内采用手机短消息,长度在 100-1000 采用站内短消息,长度在 1000 以上采用 Email,那么就可以在内部方法中自己判断实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public abstract class AbstractMessage {  
/**
* 构造方法
*/
public AbstractMessage() {
//现在什么都不做了
}
/**
* 发送消息,转调实现部分的方法
* @param message 要发送的消息内容
* @param toUser 把消息发送的目的人员
*/
public void sendMessage(String message,String toUser) {
this.getImpl(message).send(message, toUser);
}
/**
* 根据消息的长度来选择合适的实现
* @param message 要发送的消息
* @return 合适的实现对象
*/
protected MessageImplementor getImpl(String message) {
MessageImplementor impl = null;
if(message == null) {
//如果没有消息,默认使用站内短消息
impl = new MessageSMS();
} else if(message.length()< 100) {
//如果消息长度在100以内,使用手机短消息
impl = new MessageMobile();
} else if(message.length()<1000) {
//如果消息长度在100-1000以内,使用站内短消息
impl = new MessageSMS();
} else {
//如果消息长度在1000以上
impl = new MessageEmail();
}
return impl;
}
}

总结:优点是客户使用简单,而且集中控制 Implementor 对象的创建和切换逻辑;缺点是要求 Abstraction 知道所有的具体的 Implementor 实现,并知道如何选择和创建它们

2、在 Abstraction 中创建缺省的 Implementor 对象

对于这种方式,实现比较简单,直接在 Abstraction 的构造方法中,创建一个缺省的 Implementor 对象,然后子类根据需要,看是直接使用还是覆盖掉。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public abstract class AbstractMessage {  
protected MessageImplementor impl;
/**
* 构造方法
*/
public AbstractMessage(){
//创建一个默认的实现
this.impl = new MessageSMS();
}
public void sendMessage(String message,String toUser){
this.impl.send(message, toUser);
}
}

3、使用抽象工厂或者是简单工厂

对于这种方式,根据具体的需要来选择,如果是想要创建一系列实现对象,那就使用抽象工厂,如果是创建单个的实现对象,那就使用简单工厂就可以了。

直接在原来创建 Implementor 对象的地方,直接调用相应的抽象工厂或者是简单工厂,来获取相应的 Implementor 对象,这种方法的优点是 Abstraction 类不用和任何一个 Implementor 类直接耦合。

4、使用 IoC/DI 的方式

对于这种方式,Abstraction 的实现就更简单了,只需要实现注入 Implementor 对象的方法就可以了,其它的 Abstraction 就不管了。

IoC/DI 容器会负责创建 Implementor 对象,并设置回到 Abstraction 对象中,使用 IoC/DI 的方式,并不会改变 AbstractionImplementor 的关系,Abstraction 同样需要持有相应的 Implementor 对象,同样会把功能委托给 Implementor 对象去实现。

3.3 桥接模式的优缺点

  1. 分离抽象和实现部分:桥接模式分离了抽象和实现部分,从而极大地提高了系统的灵活性。让抽象部分和实现部分独立开来,分别定义接口,这有助于对系统进行分层,从而产生更好的结构化的系统。对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了;
  2. 更好的扩展性:由于桥接模式把抽象和实现部分分离开了,而且分别定义接口,这就使得抽象部分和实现部分可以分别独立的扩展,而不会相互影响,从而大大的提高了系统的可扩展性。可动态切换实现;
  3. 可减少子类的个数:如文章一开头的例子,对于有两个变化纬度的情况,如果采用继承的实现方式,大约需要两个纬度上的可变化数量的乘积个子类;而采用桥接模式来实现的话,大约需要两个纬度上的可变化数量的和个子类。可以明显地减少子类的个数;

3.3 使用场景

  • 不希望抽象和实现部分使用固定的绑定关系:把抽象和实现部分分开,然后在程序运行期间来动态的设置抽象部分需要用到的具体的实现,还可以动态切换具体的实现;
  • 出现抽象部分和实现部分都应该可以扩展的情况:让抽象部分和实现部分可以独立的变化,从而可以灵活的进行单独扩展,而不是搅在一起,扩展一边会影响到另一边;
  • 希望实现部分的修改,不会对客户产生影响:客户是面向抽象的接口在运行,实现部分的修改,可以独立于抽象部分,也就不会对客户产生影响了,说对客户是透明的;
  • 采用继承的实现方案,会导致产生很多子类:可以考虑采用桥接模式,分析功能变化的原因,看看是否能分离成不同的纬度,然后通过桥接模式来分离它们,从而减少子类的数目;