跳转至

桥接模式

分类:结构型模式。

定义与理解

桥接模式的经典定义是:

将抽象部分与它的实现部分分离,使它们都可以独立地变化。

桥接模式示意图:左岸是不同电视,右岸是不同遥控器,通过桥连接

图中左岸的电视对应实现层,右岸的遥控器对应抽象层;中间的桥表示抽象层持有实现对象,并通过约定的接口委托操作。两岸可以分别增加类型,再通过这座“桥”组合使用。

“将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构”是对这一定义的通俗解释。其中“在开发时分别使用”,理解为“可以分别演化和扩展”更准确;两侧仍通过约定的实现层接口协作。

“抽象”和“实现”分别指什么

这里的“抽象”指面向使用者提供功能的高层,“实现”指完成这些功能所需的底层能力。以本文的遥控器与电视示例为例:

层次 职责 可以独立变化的维度
抽象层:遥控器 提供开关机、换台等控制功能 不同遥控器类型,如 GenericRemote、SpecialRemote
实现层:电视 实现具体设备的开关机、频道控制能力 不同电视品牌,如 Sony、LG

RemoteControl 持有一个 TV 类型的对象引用,并把具体操作委托给它。这个对象组合与委托关系,就是连接两个层次结构的“桥”。 本例的 TV 使用抽象类定义实现层契约,也可以根据设计需要使用语言中的接口。

“独立变化”体现在哪里

  • 增加一种遥控器类型时,可以利用已有的 TV 操作组合新的控制功能,搭配现有品牌电视使用。
  • 增加一种电视品牌时,实现 TV 约定的操作,就可以与现有遥控器配合使用;本例还需要在 TVFactory 中增加该品牌的创建入口。
  • 遥控器类型与电视品牌可以组合搭配,无需为每一种组合分别定义一个子类,例如“索尼普通遥控器”“LG 普通遥控器”“索尼特殊遥控器”等。

“独立变化”以双方约定的接口能够满足需求为前提。如果新的高层功能需要额外的底层能力,仍可能需要调整实现层契约。

判断是否使用桥接模式,关键是:是否存在两个需要独立变化的维度,并通过对象组合将它们连接起来。 代码中出现抽象类、接口及其实现类,本身还不足以说明采用了桥接模式。

类结构

classDiagram
    direction TB
    class Client
    class Abstraction {
        <<abstract>>
        +operation()
    }
    class RefinedAbstraction {
        +operation()
    }
    class Implementor {
        <<interface>>
        +operationImpl()
    }
    class ConcreteImplementorA {
        +operationImpl()
    }
    class ConcreteImplementorB {
        +operationImpl()
    }
    Abstraction <|-- RefinedAbstraction
    Implementor <|.. ConcreteImplementorA
    Implementor <|.. ConcreteImplementorB
    Abstraction --> Implementor : 委托
    Client ..> Abstraction : 使用

角色定义

角色 定义
Client(客户端) 使用抽象层提供的操作,无需依赖具体实现类。
Abstraction(抽象层) 定义面向客户端的操作,持有实现层接口,并将底层工作委托给实现对象。
RefinedAbstraction(扩展抽象) 继承抽象层,利用实现层接口组合或扩展高层行为,可独立于具体实现演化。
Implementor(实现层接口) 定义抽象层所需的底层操作,其接口不必与抽象层操作一一对应。
ConcreteImplementorA(具体实现 A) 实现底层操作,提供一种可与抽象层搭配的实现方式。
ConcreteImplementorB(具体实现 B) 提供另一种实现方式,与 A 处于同一实现层次,可独立于抽象层扩展。

Head First 示例:遥控器与不同品牌电视

源码中的 RemoteControl 和 TV 都是抽象类。前者提供开关机、换台与选择电视的具体方法,后者声明电视需要实现的底层操作。GenericRemote 与 SpecialRemote 组合这些操作提供不同名称的换台按键;Sony 与 LG 则分别维护自己的频道字段。TVFactory 负责创建电视,是连接两侧的辅助对象。

具体类名 对应角色 职责
RemoteControl 抽象层 持有 TV tv 与 TVFactory tvFactory,将开关机和频道操作委托给电视。
GenericRemote 扩展抽象 提供 nextChannel()、prevChannel(),组合读取频道与设置频道。
SpecialRemote 扩展抽象 提供 up()、down(),同样复用基类的频道操作。
TV 实现层抽象类 声明 on()、off()、tuneChannel() 和 getChannel()。
Sony 具体实现 用 station 字段保存频道,实现电视操作。
LG 具体实现 用 channel 字段保存频道,实现电视操作。
TVFactory 实现对象工厂 根据 getTV(String type) 的参数创建对应品牌的电视。
Client 示例入口 演示 SpecialRemote 搭配 Sony、GenericRemote 搭配 LG。

示例 UML

classDiagram
    direction TB
    class RemoteControl {
        <<abstract>>
        TV tv
        TVFactory tvFactory
        +on()
        +off()
        +setChannel(int channel)
        +getChannel() int
        +setTV(String type)
    }
    class GenericRemote {
        +nextChannel()
        +prevChannel()
    }
    class SpecialRemote {
        +up()
        +down()
    }
    class TV {
        <<abstract>>
        +on()*
        +off()*
        +tuneChannel(int channel)*
        +getChannel() int*
    }
    class Sony {
        int station
        +tuneChannel(int channel)
        +getChannel() int
    }
    class LG {
        int channel
        +tuneChannel(int channel)
        +getChannel() int
    }
    class TVFactory {
        +getTV(String type) TV
    }
    class Client {
        +main(String[] args)$
    }
    RemoteControl <|-- GenericRemote
    RemoteControl <|-- SpecialRemote
    TV <|-- Sony
    TV <|-- LG
    RemoteControl --> TV : tv 委托
    RemoteControl --> TVFactory : tvFactory 选择电视
    TVFactory ..> Sony : 创建
    TVFactory ..> LG : 创建
    Client ..> SpecialRemote : 使用
    Client ..> GenericRemote : 使用
    Client ..> TVFactory : 创建并传入

图中省略构造器、异常处理和具体电视重复的开关机方法。RemoteControl 到 TV 的关联是桥接的核心;工厂只负责构造实现对象,遥控器的操作始终经由 TV 抽象类委托。

协作过程

  1. Client 创建 TVFactory,将它传入 SpecialRemote 的构造器,再调用 setTV("Sony");工厂创建 Sony,由遥控器保存到 tv 字段。
  2. remoteSony.on() 经 RemoteControl.on() 转交给 Sony.on()。
  3. SpecialRemote.up() 先通过 getChannel() 读取当前频道,再通过 setChannel() 将加一后的频道传给电视的 tuneChannel();down() 对应减一。
  4. 入口随后创建 GenericRemote,调用 setTV("LG"),用 nextChannel()、prevChannel() 完成同类操作。两种遥控器都能通过 setTV() 选择工厂支持的品牌,使用前需要先完成这一步。

示例源码

TV.java
1
2
3
4
5
6
7
8
package headfirst.designpatterns.bridge.remote;

public abstract class TV {
    public abstract void on();
    public abstract void off();
    public abstract void tuneChannel(int channel);
    public abstract int getChannel();
}
Sony.java
package headfirst.designpatterns.bridge.remote;

public class Sony extends TV {
    int station = 0;
    public void on() {
        System.out.println("Turning on the Sony TV");
    }
    public void off() {
        System.out.println("Turning off the Sony TV");
    }
    public void tuneChannel(int channel) {
        this.station = channel;
        System.out.println("Set the Sony TV station to " + this.station);
    }
    public int getChannel() {
        return station;
    }
}
LG.java
package headfirst.designpatterns.bridge.remote;

public class LG extends TV {
    int channel = 1;
    public void on() {
        System.out.println("Turning on the LG TV");
    }
    public void off() {
        System.out.println("Turning off the LG TV");
    }
    public void tuneChannel(int channel) {
        this.channel = channel;
        System.out.println("Set the LG TV Channel to " + this.channel);
    }
    public int getChannel() {
        return channel;
    }
}
TVFactory.java
package headfirst.designpatterns.bridge.remote;

public class TVFactory {
    public TV getTV(String type) throws Exception {
        if (type.equals("LG")) {
            return new LG();
        } else if (type.equals("Sony")) {
            return new Sony();
        } else {
            throw new Exception("Invalid TV Type");
        }
    }
}
RemoteControl.java
package headfirst.designpatterns.bridge.remote;

public abstract class RemoteControl {
    TV tv;
    TVFactory tvFactory;
    public RemoteControl(TVFactory tvFactory) {
        this.tvFactory = tvFactory;
    }
    public void on() {
        this.tv.on();
    }
    public void off() {
        this.tv.off();
    }
    public void setChannel(int channel) {
        tv.tuneChannel(channel);
    }
    public int getChannel() {
        return tv.getChannel();
    }
    public void setTV(String type) {
        try {
            tv = tvFactory.getTV(type);
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}
GenericRemote.java
package headfirst.designpatterns.bridge.remote;

public class GenericRemote extends RemoteControl {
    public GenericRemote(TVFactory tvFactory) {
        super(tvFactory);
    }
    public void nextChannel() {
        int channel = this.getChannel();
        this.setChannel(channel+1);
    }
    public void prevChannel() {
        int channel = this.getChannel();
        this.setChannel(channel-1);
    }
}
SpecialRemote.java
package headfirst.designpatterns.bridge.remote;

public class SpecialRemote extends RemoteControl {
    public SpecialRemote(TVFactory tvFactory) {
        super(tvFactory);
    }
    public void up() {
        int channel = this.getChannel();
        this.setChannel(channel+1);
    }
    public void down() {
        int channel = this.getChannel();
        this.setChannel(channel-1);
    }
}
Client.java
package headfirst.designpatterns.bridge.remote;

public class Client {
    public static void main(String[] args) {
        TVFactory tvFactory = new TVFactory();
        SpecialRemote remoteSony = new SpecialRemote(tvFactory);
        System.out.println("Connect your remote to the TV");
        remoteSony.setTV("Sony");
        remoteSony.on();
        remoteSony.up();
        remoteSony.down();
        remoteSony.off();

        GenericRemote remoteLG = new GenericRemote(tvFactory);
        System.out.println("Connect your remote to the TV");
        remoteLG.setTV("LG");
        remoteLG.on();
        remoteLG.nextChannel();
        remoteLG.prevChannel();
        remoteLG.off();
    }
}

源码来源:Head First Design Patterns / bridge/remote。

评论