https://refactoringguru.cn/design-patterns/bridge

结构型模式-桥接模式

亦称: Bridge

意图

桥接模式是一种结构型设计模式, 可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构, 从而能在开发时分别使用。

问题

抽象? 实现? 听上去挺吓人? 让我们慢慢来, 先考虑一个简单的例子。

假如你有一个几何 形状Shape类, 从它能扩展出两个子类: 圆形Circle和 方形Square 。 你希望对这样的类层次结构进行扩展以使其包含颜色, 所以你打算创建名为 红色Red和 蓝色Blue的形状子类。 但是, 由于你已有两个子类, 所以总共需要创建四个类才能覆盖所有组合, 例如 蓝色圆形Blue­Circle和 红色方形Red­Square 。

所有组合类的数量将以几何级数增长。

在层次结构中新增形状和颜色将导致代码复杂程度指数增长。 例如添加三角形状, 你需要新增两个子类, 也就是每种颜色一个; 此后新增一种新颜色需要新增三个子类, 即每种形状一个。 如此以往, 情况会越来越糟糕。

解决方案

问题的根本原因是我们试图在两个独立的维度——形状与颜色——上扩展形状类。 这在处理类继承时是很常见的问题。

桥接模式通过将继承改为组合的方式来解决这个问题。 具体来说, 就是抽取其中一个维度并使之成为独立的类层次, 这样就可以在初始类中引用这个新层次的对象, 从而使得一个类不必拥有所有的状态和行为。

将一个类层次转化为多个相关的类层次, 避免单个类层次的失控。

根据该方法, 我们可以将颜色相关的代码抽取到拥有 红色蓝色两个子类的颜色类中, 然后在 形状类中添加一个指向某一颜色对象的引用成员变量。 现在, 形状类可以将所有与颜色相关的工作委派给连入的颜色对象。 这样的引用就成为了 形状颜色之间的桥梁。 此后, 新增颜色将不再需要修改形状的类层次, 反之亦然。

抽象部分和实现部分

设计模式四人组的著作在桥接定义中提出了抽象部分实现部分两个术语。 我觉得这些术语过于学术了, 反而让模式看上去比实际情况更加复杂。 在介绍过形状和颜色的简单例子后, 我们来看看四人组著作中让人望而生畏的词语的含义。

抽象部分 (也被称为接口) 是一些实体的高阶控制层。 该层自身不完成任何具体的工作, 它需要将工作委派给实现部分层 (也被称为平台)。

注意, 这里提到的内容与编程语言中的接口抽象类无关。 它们并不是一回事。

在实际的程序中, 抽象部分是图形用户界面 (GUI), 而实现部分则是底层操作系统代码 (API), GUI 层调用 API 层来对用户的各种操作做出响应。

一般来说, 你可以在两个独立方向上扩展这种应用:

  • 开发多个不同的 GUI (例如面向普通用户和管理员进行分别配置)
  • 支持多个不同的 API (例如, 能够在 Windows、 Linux 和 macOS 上运行该程序)。

在最糟糕的情况下, 程序可能会是一团乱麻, 其中包含数百种条件语句, 连接着代码各处不同种类的 GUI 和各种 API。

在庞杂的代码中, 即使是很小的改动都非常难以完成, 因为你必须要在*整体上*对代码有充分的理解。 而在较小且定义明确的模块中, 进行修改则要容易得多

你可以将特定接口-平台的组合代码抽取到独立的类中, 以在混乱中建立一些秩序。 但是, 你很快会发现这种类的数量很多。 类层次将以指数形式增长, 因为每次添加一个新的 GUI 或支持一种新的 API 都需要创建更多的类。

让我们试着用桥接模式来解决这个问题。 该模式建议将类拆分为两个类层次结构:

  • 抽象部分: 程序的 GUI 层。
  • 实现部分: 操作系统的 API。

创建跨平台应用程序的一种方法

抽象对象控制程序的外观, 并将真实工作委派给连入的实现对象。 不同的实现只要遵循相同的接口就可以互换, 使同一 GUI 可在 Windows 和 Linux 下运行。

最后的结果是: 你无需改动与 API 相关的类就可以修改 GUI 类。 此外如果想支持一个新的操作系统, 只需在实现部分层次中创建一个子类即可。

桥接模式结构

  1. 抽象部分 (Abstraction) 提供高层控制逻辑, 依赖于完成底层实际工作的实现对象。

  2. 实现部分 (Implementation) 为所有具体实现声明通用接口。 抽象部分仅能通过在这里声明的方法与实现对象交互。

    抽象部分可以列出和实现部分一样的方法, 但是抽象部分通常声明一些复杂行为, 这些行为依赖于多种由实现部分声明的原语操作。

  3. 具体实现 (Concrete Implementations) 中包括特定于平台的代码。

  4. 精确抽象 (Refined Abstraction) 提供控制逻辑的变体。 与其父类一样, 它们通过通用实现接口与不同的实现进行交互。

  5. 通常情况下, 客户端 (Client) 仅关心如何与抽象部分合作。 但是, 客户端需要将抽象对象与一个实现对象连接起来。

伪代码

示例演示了桥接模式如何拆分程序中同时管理设备及其遥控器的庞杂代码。 设备Device类作为实现部分, 而 遥控器Remote类则作为抽象部分。

最初类层次结构被拆分为两个部分: 设备和遥控器

遥控器基类声明了一个指向设备对象的引用成员变量。 所有遥控器通过通用设备接口与设备进行交互, 使得同一个遥控器可以支持不同类型的设备。

你可以开发独立于设备类的遥控器类, 只需新建一个遥控器子类即可。 例如, 基础遥控器可能只有两个按钮, 但你可在其基础上扩展新功能, 比如额外的一节电池或一块触摸屏。

客户端代码通过遥控器构造函数将特定种类的遥控器与设备对象连接起来。

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
46
47
48
49
50
51
52
53
54
55
// “抽象部分”定义了两个类层次结构中“控制”部分的接口。它管理着一个指向“实
// 现部分”层次结构中对象的引用,并会将所有真实工作委派给该对象。
class RemoteControl is
protected field device: Device
constructor RemoteControl(device: Device) is
this.device = device
method togglePower() is
if (device.isEnabled()) then
device.disable()
else
device.enable()
method volumeDown() is
device.setVolume(device.getVolume() - 10)
method volumeUp() is
device.setVolume(device.getVolume() + 10)
method channelDown() is
device.setChannel(device.getChannel() - 1)
method channelUp() is
device.setChannel(device.getChannel() + 1)


// 你可以独立于设备类的方式从抽象层中扩展类。
class AdvancedRemoteControl extends RemoteControl is
method mute() is
device.setVolume(0)


// “实现部分”接口声明了在所有具体实现类中通用的方法。它不需要与抽象接口相
// 匹配。实际上,这两个接口可以完全不一样。通常实现接口只提供原语操作,而
// 抽象接口则会基于这些操作定义较高层次的操作。
interface Device is
method isEnabled()
method enable()
method disable()
method getVolume()
method setVolume(percent)
method getChannel()
method setChannel(channel)


// 所有设备都遵循相同的接口。
class Tv implements Device is
// ...

class Radio implements Device is
// ...


// 客户端代码中的某个位置。
tv = new Tv()
remote = new RemoteControl(tv)
remote.togglePower()

radio = new Radio()
remote = new AdvancedRemoteControl(radio)

桥接模式适合应用场景

如果你想要拆分或重组一个具有多重功能的庞杂类(例如能与多个数据库服务器进行交互的类),可以使用桥接模式。

类的代码行数越多, 弄清其运作方式就越困难, 对其进行修改所花费的时间就越长。 一个功能上的变化可能需要在整个类范围内进行修改, 而且常常会产生错误, 甚至还会有一些严重的副作用。

桥接模式可以将庞杂类拆分为几个类层次结构。 此后, 你可以修改任意一个类层次结构而不会影响到其他类层次结构。 这种方法可以简化代码的维护工作, 并将修改已有代码的风险降到最低。

如果你希望在几个独立维度上扩展一个类,可使用该模式。

桥接建议将每个维度抽取为独立的类层次。 初始类将相关工作委派给属于对应类层次的对象, 无需自己完成所有工作。

如果你需要在运行时切换不同实现方法,可使用桥接模式。

当然并不是说一定要实现这一点, 桥接模式可替换抽象部分中的实现对象, 具体操作就和给成员变量赋新值一样简单。

顺便提一句, 最后一点是很多人混淆桥接模式和策略模式的主要原因。 记住, 设计模式并不仅是一种对类进行组织的方式, 它还能用于沟通意图和解决问题。

实现方式

  1. 明确类中独立的维度。 独立的概念可能是: 抽象/平台, 域/基础设施, 前端/后端或接口/实现。
  2. 了解客户端的业务需求, 并在抽象基类中定义它们。
  3. 确定在所有平台上都可执行的业务。 并在通用实现接口中声明抽象部分所需的业务。
  4. 为你域内的所有平台创建实现类, 但需确保它们遵循实现部分的接口。
  5. 在抽象类中添加指向实现类型的引用成员变量。 抽象部分会将大部分工作委派给该成员变量所指向的实现对象。
  6. 如果你的高层逻辑有多个变体, 则可通过扩展抽象基类为每个变体创建一个精确抽象。
  7. 客户端代码必须将实现对象传递给抽象部分的构造函数才能使其能够相互关联。 此后, 客户端只需与抽象对象进行交互, 无需和实现对象打交道。

桥接模式优缺点

优点

  • 你可以创建与平台无关的类和程序。
  • 客户端代码仅与高层抽象部分进行互动, 不会接触到平台的详细信息。
  • 开闭原则。 你可以新增抽象部分和实现部分, 且它们之间不会相互影响。
  • 单一职责原则。 抽象部分专注于处理高层逻辑, 实现部分处理平台细节。

缺点

  • 对高内聚的类使用该模式可能会让代码更加复杂。

与其他模式的关系

  • 桥接模式通常会于开发前期进行设计, 使你能够将程序的各个部分独立开来以便开发。 另一方面, 适配器模式通常在已有程序中使用, 让相互不兼容的类能很好地合作。
  • 桥接状态模式策略模式 (在某种程度上包括适配器) 模式的接口非常相似。 实际上, 它们都基于组合模式——即将工作委派给其他对象, 不过也各自解决了不同的问题。 模式并不只是以特定方式组织代码的配方, 你还可以使用它们来和其他开发者讨论模式所解决的问题。
  • 你可以将抽象工厂模式桥接搭配使用。 如果由桥接定义的抽象只能与特定实现合作, 这一模式搭配就非常有用。 在这种情况下, 抽象工厂可以对这些关系进行封装, 并且对客户端代码隐藏其复杂性。
  • 你可以结合使用生成器模式桥接模式主管类负责抽象工作, 各种不同的生成器负责实现工作。

代码示例

桥接是一种结构型设计模式, 可将业务逻辑或一个大类拆分为不同的层次结构, 从而能独立地进行开发。

层次结构中的第一层 (通常称为抽象部分) 将包含对第二层 (实现部分) 对象的引用。 抽象部分将能将一些 (有时是绝大部分) 对自己的调用委派给实现部分的对象。 所有的实现部分都有一个通用接口, 因此它们能在抽象部分内部相互替换。

在 C# 中使用模式

复杂度: ★★★

流行度: ★☆☆

使用示例: 桥接模式在处理跨平台应用、 支持多种类型的数据库服务器或与多个特定种类 (例如云平台和社交网络等) 的 API 供应商协作时会特别有用。

识别方法: 桥接可以通过一些控制实体及其所依赖的多个不同平台之间的明确区别来进行识别。

概念示例

本例说明了桥接设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

Program.cs: 概念示例

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
using System;

namespace RefactoringGuru.DesignPatterns.Bridge.Conceptual
{
// The Abstraction defines the interface for the "control" part of the two
// class hierarchies. It maintains a reference to an object of the
// Implementation hierarchy and delegates all of the real work to this
// object.
class Abstraction
{
protected IImplementation _implementation;

public Abstraction(IImplementation implementation)
{
this._implementation = implementation;
}

public virtual string Operation()
{
return "Abstract: Base operation with:\n" +
_implementation.OperationImplementation();
}
}

// You can extend the Abstraction without changing the Implementation
// classes.
class ExtendedAbstraction : Abstraction
{
public ExtendedAbstraction(IImplementation implementation) : base(implementation)
{
}

public override string Operation()
{
return "ExtendedAbstraction: Extended operation with:\n" +
base._implementation.OperationImplementation();
}
}

// The Implementation defines the interface for all implementation classes.
// It doesn't have to match the Abstraction's interface. In fact, the two
// interfaces can be entirely different. Typically the Implementation
// interface provides only primitive operations, while the Abstraction
// defines higher- level operations based on those primitives.
public interface IImplementation
{
string OperationImplementation();
}

// Each Concrete Implementation corresponds to a specific platform and
// implements the Implementation interface using that platform's API.
class ConcreteImplementationA : IImplementation
{
public string OperationImplementation()
{
return "ConcreteImplementationA: The result in platform A.\n";
}
}

class ConcreteImplementationB : IImplementation
{
public string OperationImplementation()
{
return "ConcreteImplementationA: The result in platform B.\n";
}
}

class Client
{
// Except for the initialization phase, where an Abstraction object gets
// linked with a specific Implementation object, the client code should
// only depend on the Abstraction class. This way the client code can
// support any abstraction-implementation combination.
public void ClientCode(Abstraction abstraction)
{
Console.Write(abstraction.Operation());
}
}

class Program
{
static void Main(string[] args)
{
Client client = new Client();

Abstraction abstraction;
// The client code should be able to work with any pre-configured
// abstraction-implementation combination.
abstraction = new Abstraction(new ConcreteImplementationA());
client.ClientCode(abstraction);

Console.WriteLine();

abstraction = new ExtendedAbstraction(new ConcreteImplementationB());
client.ClientCode(abstraction);
}
}
}

Output.txt: 执行结果

1
2
3
4
5
Abstract: Base operation with:
ConcreteImplementationA: The result in platform A.

ExtendedAbstraction: Extended operation with:
ConcreteImplementationA: The result in platform B.

在 C++ 中使用模式

复杂度: ★★★

流行度: ★☆☆

使用示例: 桥接模式在处理跨平台应用、 支持多种类型的数据库服务器或与多个特定种类 (例如云平台和社交网络等) 的 API 供应商协作时会特别有用。

识别方法: 桥接可以通过一些控制实体及其所依赖的多个不同平台之间的明确区别来进行识别。

概念示例

本例说明了桥接设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

main.cc: 概念示例

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/**
* The Implementation defines the interface for all implementation classes. It
* doesn't have to match the Abstraction's interface. In fact, the two
* interfaces can be entirely different. Typically the Implementation interface
* provides only primitive operations, while the Abstraction defines higher-
* level operations based on those primitives.
*/

class Implementation {
public:
virtual ~Implementation() {}
virtual std::string OperationImplementation() const = 0;
};

/**
* Each Concrete Implementation corresponds to a specific platform and
* implements the Implementation interface using that platform's API.
*/
class ConcreteImplementationA : public Implementation {
public:
std::string OperationImplementation() const override {
return "ConcreteImplementationA: Here's the result on the platform A.\n";
}
};
class ConcreteImplementationB : public Implementation {
public:
std::string OperationImplementation() const override {
return "ConcreteImplementationB: Here's the result on the platform B.\n";
}
};

/**
* The Abstraction defines the interface for the "control" part of the two class
* hierarchies. It maintains a reference to an object of the Implementation
* hierarchy and delegates all of the real work to this object.
*/

class Abstraction {
/**
* @var Implementation
*/
protected:
Implementation* implementation_;

public:
Abstraction(Implementation* implementation) : implementation_(implementation) {
}

virtual ~Abstraction() {
}

virtual std::string Operation() const {
return "Abstraction: Base operation with:\n" +
this->implementation_->OperationImplementation();
}
};
/**
* You can extend the Abstraction without changing the Implementation classes.
*/
class ExtendedAbstraction : public Abstraction {
public:
ExtendedAbstraction(Implementation* implementation) : Abstraction(implementation) {
}
std::string Operation() const override {
return "ExtendedAbstraction: Extended operation with:\n" +
this->implementation_->OperationImplementation();
}
};

/**
* Except for the initialization phase, where an Abstraction object gets linked
* with a specific Implementation object, the client code should only depend on
* the Abstraction class. This way the client code can support any abstraction-
* implementation combination.
*/
void ClientCode(const Abstraction& abstraction) {
// ...
std::cout << abstraction.Operation();
// ...
}
/**
* The client code should be able to work with any pre-configured abstraction-
* implementation combination.
*/

int main() {
Implementation* implementation = new ConcreteImplementationA;
Abstraction* abstraction = new Abstraction(implementation);
ClientCode(*abstraction);
std::cout << std::endl;
delete implementation;
delete abstraction;

implementation = new ConcreteImplementationB;
abstraction = new ExtendedAbstraction(implementation);
ClientCode(*abstraction);

delete implementation;
delete abstraction;

return 0;
}

Output.txt: 执行结果

1
2
3
4
5
Abstraction: Base operation with:
ConcreteImplementationA: Here's the result on the platform A.

ExtendedAbstraction: Extended operation with:
ConcreteImplementationB: Here's the result on the platform B.

在 Java 中使用模式

复杂度: ★★★

流行度: ★☆☆

使用示例: 桥接模式在处理跨平台应用、 支持多种类型的数据库服务器或与多个特定种类 (例如云平台和社交网络等) 的 API 供应商协作时会特别有用。

识别方法: 桥接可以通过一些控制实体及其所依赖的多个不同平台之间的明确区别来进行识别。

设备和远程控制之间的桥接

本例展示了远程控制器及其所控制的设备的类之间的分离。

远程控制器是抽象部分, 设备则是其实现部分。 由于有通用的接口, 同一远程控制器可与不同的设备合作, 反过来也一样。

桥接模式允许在不改动另一层次代码的前提下修改已有类, 甚至创建新类。

devices

devices/Device.java: 所有设备的通用接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package refactoring_guru.bridge.example.devices;

public interface Device {
boolean isEnabled();

void enable();

void disable();

int getVolume();

void setVolume(int percent);

int getChannel();

void setChannel(int channel);

void printStatus();
}

devices/Radio.java: 收音机

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
46
47
48
49
50
51
52
53
54
55
56
57
58
package refactoring_guru.bridge.example.devices;

public class Radio implements Device {
private boolean on = false;
private int volume = 30;
private int channel = 1;

@Override
public boolean isEnabled() {
return on;
}

@Override
public void enable() {
on = true;
}

@Override
public void disable() {
on = false;
}

@Override
public int getVolume() {
return volume;
}

@Override
public void setVolume(int volume) {
if (volume > 100) {
this.volume = 100;
} else if (volume < 0) {
this.volume = 0;
} else {
this.volume = volume;
}
}

@Override
public int getChannel() {
return channel;
}

@Override
public void setChannel(int channel) {
this.channel = channel;
}

@Override
public void printStatus() {
System.out.println("------------------------------------");
System.out.println("| I'm radio.");
System.out.println("| I'm " + (on ? "enabled" : "disabled"));
System.out.println("| Current volume is " + volume + "%");
System.out.println("| Current channel is " + channel);
System.out.println("------------------------------------\n");
}
}

devices/Tv.java: 电视机

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
46
47
48
49
50
51
52
53
54
55
56
57
58
package refactoring_guru.bridge.example.devices;

public class Tv implements Device {
private boolean on = false;
private int volume = 30;
private int channel = 1;

@Override
public boolean isEnabled() {
return on;
}

@Override
public void enable() {
on = true;
}

@Override
public void disable() {
on = false;
}

@Override
public int getVolume() {
return volume;
}

@Override
public void setVolume(int volume) {
if (volume > 100) {
this.volume = 100;
} else if (volume < 0) {
this.volume = 0;
} else {
this.volume = volume;
}
}

@Override
public int getChannel() {
return channel;
}

@Override
public void setChannel(int channel) {
this.channel = channel;
}

@Override
public void printStatus() {
System.out.println("------------------------------------");
System.out.println("| I'm TV set.");
System.out.println("| I'm " + (on ? "enabled" : "disabled"));
System.out.println("| Current volume is " + volume + "%");
System.out.println("| Current channel is " + channel);
System.out.println("------------------------------------\n");
}
}

remotes

remotes/Remote.java: 所有远程控制器的通用接口

1
2
3
4
5
6
7
8
9
10
11
12
13
package refactoring_guru.bridge.example.remotes;

public interface Remote {
void power();

void volumeDown();

void volumeUp();

void channelDown();

void channelUp();
}

remotes/BasicRemote.java: 基础远程控制器

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
46
47
package refactoring_guru.bridge.example.remotes;

import refactoring_guru.bridge.example.devices.Device;

public class BasicRemote implements Remote {
protected Device device;

public BasicRemote() {}

public BasicRemote(Device device) {
this.device = device;
}

@Override
public void power() {
System.out.println("Remote: power toggle");
if (device.isEnabled()) {
device.disable();
} else {
device.enable();
}
}

@Override
public void volumeDown() {
System.out.println("Remote: volume down");
device.setVolume(device.getVolume() - 10);
}

@Override
public void volumeUp() {
System.out.println("Remote: volume up");
device.setVolume(device.getVolume() + 10);
}

@Override
public void channelDown() {
System.out.println("Remote: channel down");
device.setChannel(device.getChannel() - 1);
}

@Override
public void channelUp() {
System.out.println("Remote: channel up");
device.setChannel(device.getChannel() + 1);
}
}

remotes/AdvancedRemote.java: 高级远程控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package refactoring_guru.bridge.example.remotes;

import refactoring_guru.bridge.example.devices.Device;

public class AdvancedRemote extends BasicRemote {

public AdvancedRemote(Device device) {
super.device = device;
}

public void mute() {
System.out.println("Remote: mute");
device.setVolume(0);
}
}

Demo.java: 客户端代码

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
package refactoring_guru.bridge.example;

import refactoring_guru.bridge.example.devices.Device;
import refactoring_guru.bridge.example.devices.Radio;
import refactoring_guru.bridge.example.devices.Tv;
import refactoring_guru.bridge.example.remotes.AdvancedRemote;
import refactoring_guru.bridge.example.remotes.BasicRemote;

public class Demo {
public static void main(String[] args) {
testDevice(new Tv());
testDevice(new Radio());
}

public static void testDevice(Device device) {
System.out.println("Tests with basic remote.");
BasicRemote basicRemote = new BasicRemote(device);
basicRemote.power();
device.printStatus();

System.out.println("Tests with advanced remote.");
AdvancedRemote advancedRemote = new AdvancedRemote(device);
advancedRemote.power();
advancedRemote.mute();
device.printStatus();
}
}

OutputDemo.txt: 执行结果

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
Tests with basic remote.
Remote: power toggle
------------------------------------
| I'm TV set.
| I'm enabled
| Current volume is 30%
| Current channel is 1
------------------------------------

Tests with advanced remote.
Remote: power toggle
Remote: mute
------------------------------------
| I'm TV set.
| I'm disabled
| Current volume is 0%
| Current channel is 1
------------------------------------

Tests with basic remote.
Remote: power toggle
------------------------------------
| I'm radio.
| I'm enabled
| Current volume is 30%
| Current channel is 1
------------------------------------

Tests with advanced remote.
Remote: power toggle
Remote: mute
------------------------------------
| I'm radio.
| I'm disabled
| Current volume is 0%
| Current channel is 1
------------------------------------

在 PHP 中使用模式

复杂度: ★★★

流行度: ★☆☆

使用示例: 桥接模式在支持多种类型的数据库服务器或与多个特定种类 (例如云平台和社交网络等) 的 API 供应商协作时会特别有用。

识别方法: 桥接可以通过一些控制实体及其所依赖的多个不同平台之间的明确区别来进行识别。

概念示例

本例说明了桥接设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

了解该模式的结构后, 你可以更容易地理解下面基于真实世界的 PHP 应用案例。

index.php: 概念示例

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<?php

namespace RefactoringGuru\Bridge\Conceptual;

/**
* The Abstraction defines the interface for the "control" part of the two class
* hierarchies. It maintains a reference to an object of the Implementation
* hierarchy and delegates all of the real work to this object.
*/
class Abstraction
{
/**
* @var Implementation
*/
protected $implementation;

public function __construct(Implementation $implementation)
{
$this->implementation = $implementation;
}

public function operation(): string
{
return "Abstraction: Base operation with:\n" .
$this->implementation->operationImplementation();
}
}

/**
* You can extend the Abstraction without changing the Implementation classes.
*/
class ExtendedAbstraction extends Abstraction
{
public function operation(): string
{
return "ExtendedAbstraction: Extended operation with:\n" .
$this->implementation->operationImplementation();
}
}

/**
* The Implementation defines the interface for all implementation classes. It
* doesn't have to match the Abstraction's interface. In fact, the two
* interfaces can be entirely different. Typically the Implementation interface
* provides only primitive operations, while the Abstraction defines higher-
* level operations based on those primitives.
*/
interface Implementation
{
public function operationImplementation(): string;
}

/**
* Each Concrete Implementation corresponds to a specific platform and
* implements the Implementation interface using that platform's API.
*/
class ConcreteImplementationA implements Implementation
{
public function operationImplementation(): string
{
return "ConcreteImplementationA: Here's the result on the platform A.\n";
}
}

class ConcreteImplementationB implements Implementation
{
public function operationImplementation(): string
{
return "ConcreteImplementationB: Here's the result on the platform B.\n";
}
}

/**
* Except for the initialization phase, where an Abstraction object gets linked
* with a specific Implementation object, the client code should only depend on
* the Abstraction class. This way the client code can support any abstraction-
* implementation combination.
*/
function clientCode(Abstraction $abstraction)
{
// ...

echo $abstraction->operation();

// ...
}

/**
* The client code should be able to work with any pre-configured abstraction-
* implementation combination.
*/
$implementation = new ConcreteImplementationA();
$abstraction = new Abstraction($implementation);
clientCode($abstraction);

echo "\n";

$implementation = new ConcreteImplementationB();
$abstraction = new ExtendedAbstraction($implementation);
clientCode($abstraction);

Output.txt: 执行结果

1
2
3
4
5
Abstraction: Base operation with:
ConcreteImplementationA: Here's the result on the platform A.

ExtendedAbstraction: Extended operation with:
ConcreteImplementationB: Here's the result on the platform B.

真实世界示例

在本例中, 页面层作为抽象部分, 而渲染层则作为实现部分。 页面类的对象可用附加在该页面的渲染对象提供的基础元素合成特定种类的网页。 由于两个层次相互独立, 你可以在不修改页面层的前提下添加一个新的渲染层类, 反之亦然。

index.php: 真实世界示例

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
<?php

namespace RefactoringGuru\Bridge\RealWorld;

/**
* The Abstraction.
*/
abstract class Page
{
/**
* @var Renderer
*/
protected $renderer;

/**
* The Abstraction is usually initialized with one of the Implementation
* objects.
*/
public function __construct(Renderer $renderer)
{
$this->renderer = $renderer;
}

/**
* The Bridge pattern allows replacing the attached Implementation object
* dynamically.
*/
public function changeRenderer(Renderer $renderer): void
{
$this->renderer = $renderer;
}

/**
* The "view" behavior stays abstract since it can only be provided by
* Concrete Abstraction classes.
*/
abstract public function view(): string;
}

/**
* This Concrete Abstraction represents a simple page.
*/
class SimplePage extends Page
{
protected $title;
protected $content;

public function __construct(Renderer $renderer, string $title, string $content)
{
parent::__construct($renderer);
$this->title = $title;
$this->content = $content;
}

public function view(): string
{
return $this->renderer->renderParts([
$this->renderer->renderHeader(),
$this->renderer->renderTitle($this->title),
$this->renderer->renderTextBlock($this->content),
$this->renderer->renderFooter()
]);
}
}

/**
* This Concrete Abstraction represents a more complex page.
*/
class ProductPage extends Page
{
protected $product;

public function __construct(Renderer $renderer, Product $product)
{
parent::__construct($renderer);
$this->product = $product;
}

public function view(): string
{
return $this->renderer->renderParts([
$this->renderer->renderHeader(),
$this->renderer->renderTitle($this->product->getTitle()),
$this->renderer->renderTextBlock($this->product->getDescription()),
$this->renderer->renderImage($this->product->getImage()),
$this->renderer->renderLink("/cart/add/" . $this->product->getId(), "Add to cart"),
$this->renderer->renderFooter()
]);
}
}

/**
* A helper class for the ProductPage class.
*/
class Product
{
private $id, $title, $description, $image, $price;

public function __construct(
string $id,
string $title,
string $description,
string $image,
float $price
) {
$this->id = $id;
$this->title = $title;
$this->description = $description;
$this->image = $image;
$this->price = $price;
}

public function getId(): string { return $this->id; }

public function getTitle(): string { return $this->title; }

public function getDescription(): string { return $this->description; }

public function getImage(): string { return $this->image; }

public function getPrice(): float { return $this->price; }
}


/**
* The Implementation declares a set of "real", "under-the-hood", "platform"
* methods.
*
* In this case, the Implementation lists rendering methods that can be used to
* compose any web page. Different Abstractions may use different methods of the
* Implementation.
*/
interface Renderer
{
public function renderTitle(string $title): string;

public function renderTextBlock(string $text): string;

public function renderImage(string $url): string;

public function renderLink(string $url, string $title): string;

public function renderHeader(): string;

public function renderFooter(): string;

public function renderParts(array $parts): string;
}

/**
* This Concrete Implementation renders a web page as HTML.
*/
class HTMLRenderer implements Renderer
{
public function renderTitle(string $title): string
{
return "<h1>$title</h1>";
}

public function renderTextBlock(string $text): string
{
return "<div class='text'>$text</div>";
}

public function renderImage(string $url): string
{
return "<img src='$url'>";
}

public function renderLink(string $url, string $title): string
{
return "<a href='$url'>$title</a>";
}

public function renderHeader(): string
{
return "<html><body>";
}

public function renderFooter(): string
{
return "</body></html>";
}

public function renderParts(array $parts): string
{
return implode("\n", $parts);
}
}

/**
* This Concrete Implementation renders a web page as JSON strings.
*/
class JsonRenderer implements Renderer
{
public function renderTitle(string $title): string
{
return '"title": "' . $title . '"';
}

public function renderTextBlock(string $text): string
{
return '"text": "' . $text . '"';
}

public function renderImage(string $url): string
{
return '"img": "' . $url . '"';
}

public function renderLink(string $url, string $title): string
{
return '"link": {"href": "' . $url . '", "title": "' . $title . '"}';
}

public function renderHeader(): string
{
return '';
}

public function renderFooter(): string
{
return '';
}

public function renderParts(array $parts): string
{
return "{\n" . implode(",\n", array_filter($parts)) . "\n}";
}
}

/**
* The client code usually deals only with the Abstraction objects.
*/
function clientCode(Page $page)
{
// ...

echo $page->view();

// ...
}

/**
* The client code can be executed with any pre-configured combination of the
* Abstraction+Implementation.
*/
$HTMLRenderer = new HTMLRenderer();
$JSONRenderer = new JsonRenderer();

$page = new SimplePage($HTMLRenderer, "Home", "Welcome to our website!");
echo "HTML view of a simple content page:\n";
clientCode($page);
echo "\n\n";

/**
* The Abstraction can change the linked Implementation at runtime if needed.
*/
$page->changeRenderer($JSONRenderer);
echo "JSON view of a simple content page, rendered with the same client code:\n";
clientCode($page);
echo "\n\n";


$product = new Product("123", "Star Wars, episode1",
"A long time ago in a galaxy far, far away...",
"/images/star-wars.jpeg", 39.95);

$page = new ProductPage($HTMLRenderer, $product);
echo "HTML view of a product page, same client code:\n";
clientCode($page);
echo "\n\n";

$page->changeRenderer($JSONRenderer);
echo "JSON view of a simple content page, with the same client code:\n";
clientCode($page);

Output.txt: 执行结果

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
HTML view of a simple content page:
<html><body>
<h1>Home</h1>
<div class='text'>Welcome to our website!</div>
</body></html>

JSON view of a simple content page, rendered with the same client code:
{
"title": "Home",
"text": "Welcome to our website!"
}

HTML view of a product page, same client code:
<html><body>
<h1>Star Wars, episode1</h1>
<div class='text'>A long time ago in a galaxy far, far away...</div>
<img src='/images/star-wars.jpeg'>
<a href='/cart/add/123'>Add to cart</a>
</body></html>

JSON view of a simple content page, with the same client code:
{
"title": "Star Wars, episode1",
"text": "A long time ago in a galaxy far, far away...",
"img": "/images/star-wars.jpeg",
"link": {"href": "/cart/add/123", "title": "Add to cart"}
}

在 Python 中使用模式

复杂度: ★★★

流行度: ★☆☆

使用示例: 桥接模式在处理跨平台应用、 支持多种类型的数据库服务器或与多个特定种类 (例如云平台和社交网络等) 的 API 供应商协作时会特别有用。

识别方法: 桥接可以通过一些控制实体及其所依赖的多个不同平台之间的明确区别来进行识别。

概念示例

本例说明了桥接设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

main.py: 概念示例

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
from __future__ import annotations
from abc import ABC, abstractmethod


class Abstraction:
"""
The Abstraction defines the interface for the "control" part of the two
class hierarchies. It maintains a reference to an object of the
Implementation hierarchy and delegates all of the real work to this object.
"""

def __init__(self, implementation: Implementation) -> None:
self.implementation = implementation

def operation(self) -> str:
return (f"Abstraction: Base operation with:\n"
f"{self.implementation.operation_implementation()}")


class ExtendedAbstraction(Abstraction):
"""
You can extend the Abstraction without changing the Implementation classes.
"""

def operation(self) -> str:
return (f"ExtendedAbstraction: Extended operation with:\n"
f"{self.implementation.operation_implementation()}")


class Implementation(ABC):
"""
The Implementation defines the interface for all implementation classes. It
doesn't have to match the Abstraction's interface. In fact, the two
interfaces can be entirely different. Typically the Implementation interface
provides only primitive operations, while the Abstraction defines higher-
level operations based on those primitives.
"""

@abstractmethod
def operation_implementation(self) -> str:
pass


"""
Each Concrete Implementation corresponds to a specific platform and implements
the Implementation interface using that platform's API.
"""


class ConcreteImplementationA(Implementation):
def operation_implementation(self) -> str:
return "ConcreteImplementationA: Here's the result on the platform A."


class ConcreteImplementationB(Implementation):
def operation_implementation(self) -> str:
return "ConcreteImplementationB: Here's the result on the platform B."


def client_code(abstraction: Abstraction) -> None:
"""
Except for the initialization phase, where an Abstraction object gets linked
with a specific Implementation object, the client code should only depend on
the Abstraction class. This way the client code can support any abstraction-
implementation combination.
"""

# ...

print(abstraction.operation(), end="")

# ...


if __name__ == "__main__":
"""
The client code should be able to work with any pre-configured abstraction-
implementation combination.
"""

implementation = ConcreteImplementationA()
abstraction = Abstraction(implementation)
client_code(abstraction)

print("\n")

implementation = ConcreteImplementationB()
abstraction = ExtendedAbstraction(implementation)
client_code(abstraction)

Output.txt: 执行结果

1
2
3
4
5
Abstraction: Base operation with:
ConcreteImplementationA: Here's the result on the platform A.

ExtendedAbstraction: Extended operation with:
ConcreteImplementationB: Here's the result on the platform B.

在 Ruby 中使用模式

复杂度: ★★★

流行度: ★☆☆

使用示例: 桥接模式在处理跨平台应用、 支持多种类型的数据库服务器或与多个特定种类 (例如云平台和社交网络等) 的 API 供应商协作时会特别有用。

识别方法: 桥接可以通过一些控制实体及其所依赖的多个不同平台之间的明确区别来进行识别。

概念示例

本例说明了桥接设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

main.rb: 概念示例

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# The Abstraction defines the interface for the "control" part of the two class
# hierarchies. It maintains a reference to an object of the Implementation
# hierarchy and delegates all of the real work to this object.
class Abstraction
# @param [Implementation] implementation
def initialize(implementation)
@implementation = implementation
end

# @return [String]
def operation
"Abstraction: Base operation with:\n"\
"#{@implementation.operation_implementation}"
end
end

# You can extend the Abstraction without changing the Implementation classes.
class ExtendedAbstraction < Abstraction
# @return [String]
def operation
"ExtendedAbstraction: Extended operation with:\n"\
"#{@implementation.operation_implementation}"
end
end

# The Implementation defines the interface for all implementation classes. It
# doesn't have to match the Abstraction's interface. In fact, the two interfaces
# can be entirely different. Typically the Implementation interface provides
# only primitive operations, while the Abstraction defines higher-level
# operations based on those primitives.
class Implementation
# @abstract
#
# @return [String]
def operation_implementation
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end

# Each Concrete Implementation corresponds to a specific platform and implements
# the Implementation interface using that platform's API.
class ConcreteImplementationA < Implementation
# @return [String]
def operation_implementation
'ConcreteImplementationA: Here\'s the result on the platform A.'
end
end

class ConcreteImplementationB < Implementation
# @return [String]
def operation_implementation
'ConcreteImplementationB: Here\'s the result on the platform B.'
end
end

# Except for the initialization phase, where an Abstraction object gets linked
# with a specific Implementation object, the client code should only depend on
# the Abstraction class. This way the client code can support any abstraction-
# implementation combination.
def client_code(abstraction)
# ...

print abstraction.operation

# ...
end

# The client code should be able to work with any pre-configured abstraction-
# implementation combination.

implementation = ConcreteImplementationA.new
abstraction = Abstraction.new(implementation)
client_code(abstraction)

puts "\n\n"

implementation = ConcreteImplementationB.new
abstraction = ExtendedAbstraction.new(implementation)
client_code(abstraction)

output.txt: 执行结果

1
2
3
4
5
Abstraction: Base operation with:
ConcreteImplementationA: Here's the result on the platform A.

ExtendedAbstraction: Extended operation with:
ConcreteImplementationB: Here's the result on the platform B.

在 Swift 中使用模式

复杂度: ★★★

流行度: ★☆☆

使用示例: 桥接模式在处理跨平台应用、 支持多种类型的数据库服务器或与多个特定种类 (例如云平台和社交网络等) 的 API 供应商协作时会特别有用。

识别方法: 桥接可以通过一些控制实体及其所依赖的多个不同平台之间的明确区别来进行识别。

概念示例

本例说明了桥接设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

了解该模式的结构后, 你可以更容易地理解下面基于真实世界的 Swift 应用案例。

Example.swift: 概念示例

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import XCTest

/// The Abstraction defines the interface for the "control" part of the two
/// class hierarchies. It maintains a reference to an object of the
/// Implementation hierarchy and delegates all of the real work to this object.
class Abstraction {

fileprivate var implementation: Implementation

init(_ implementation: Implementation) {
self.implementation = implementation
}

func operation() -> String {
let operation = implementation.operationImplementation()
return "Abstraction: Base operation with:\n" + operation
}
}

/// You can extend the Abstraction without changing the Implementation classes.
class ExtendedAbstraction: Abstraction {

override func operation() -> String {
let operation = implementation.operationImplementation()
return "ExtendedAbstraction: Extended operation with:\n" + operation
}
}

/// The Implementation defines the interface for all implementation classes. It
/// doesn't have to match the Abstraction's interface. In fact, the two
/// interfaces can be entirely different. Typically the Implementation interface
/// provides only primitive operations, while the Abstraction defines higher-
/// level operations based on those primitives.
protocol Implementation {

func operationImplementation() -> String
}

/// Each Concrete Implementation corresponds to a specific platform and
/// implements the Implementation interface using that platform's API.
class ConcreteImplementationA: Implementation {

func operationImplementation() -> String {
return "ConcreteImplementationA: Here's the result on the platform A.\n"
}
}

class ConcreteImplementationB: Implementation {

func operationImplementation() -> String {
return "ConcreteImplementationB: Here's the result on the platform B\n"
}
}

/// Except for the initialization phase, where an Abstraction object gets linked
/// with a specific Implementation object, the client code should only depend on
/// the Abstraction class. This way the client code can support any abstraction-
/// implementation combination.
class Client {
// ...
static func someClientCode(abstraction: Abstraction) {
print(abstraction.operation())
}
// ...
}

/// Let's see how it all works together.
class BridgeConceptual: XCTestCase {

func testBridgeConceptual() {
// The client code should be able to work with any pre-configured
// abstraction-implementation combination.
let implementation = ConcreteImplementationA()
Client.someClientCode(abstraction: Abstraction(implementation))

let concreteImplementation = ConcreteImplementationB()
Client.someClientCode(abstraction: ExtendedAbstraction(concreteImplementation))
}
}

Output.txt: 执行结果

1
2
3
4
5
Abstraction: Base operation with:
ConcreteImplementationA: Here's the result on the platform A

ExtendedAbstraction: Extended operation with:
ConcreteImplementationB: Here's the result on the platform B

真实世界示例

Example.swift: 真实世界示例

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import XCTest

private class BridgeRealWorld: XCTestCase {

func testBridgeRealWorld() {

print("Client: Pushing Photo View Controller...")
push(PhotoViewController())

print()

print("Client: Pushing Feed View Controller...")
push(FeedViewController())
}

func push(_ container: SharingSupportable) {

let instagram = InstagramSharingService()
let facebook = FaceBookSharingService()

container.accept(service: instagram)
container.update(content: foodModel)

container.accept(service: facebook)
container.update(content: foodModel)
}

var foodModel: Content {
return FoodDomainModel(title: "This food is so various and delicious!",
images: [UIImage(), UIImage()],
calories: 47)
}
}

private protocol SharingSupportable {

/// Abstraction
func accept(service: SharingService)

func update(content: Content)
}

class BaseViewController: UIViewController, SharingSupportable {

fileprivate var shareService: SharingService?

func update(content: Content) {
/// ...updating UI and showing a content...
/// ...
/// ... then, a user will choose a content and trigger an event
print("\(description): User selected a \(content) to share")
/// ...
shareService?.share(content: content)
}

func accept(service: SharingService) {
shareService = service
}
}

class PhotoViewController: BaseViewController {

/// Custom UI and features

override var description: String {
return "PhotoViewController"
}
}

class FeedViewController: BaseViewController {

/// Custom UI and features

override var description: String {
return "FeedViewController"
}
}

protocol SharingService {

/// Implementation
func share(content: Content)
}

class FaceBookSharingService: SharingService {

func share(content: Content) {

/// Use FaceBook API to share a content
print("Service: \(content) was posted to the Facebook")
}
}

class InstagramSharingService: SharingService {

func share(content: Content) {

/// Use Instagram API to share a content
print("Service: \(content) was posted to the Instagram", terminator: "\n\n")
}
}

protocol Content: CustomStringConvertible {

var title: String { get }
var images: [UIImage] { get }
}

struct FoodDomainModel: Content {

var title: String
var images: [UIImage]
var calories: Int

var description: String {
return "Food Model"
}
}

Output.txt: 执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
Client: Pushing Photo View Controller...
PhotoViewController: User selected a Food Model to share
Service: Food Model was posted to the Instagram

PhotoViewController: User selected a Food Model to share
Service: Food Model was posted to the Facebook

Client: Pushing Feed View Controller...
FeedViewController: User selected a Food Model to share
Service: Food Model was posted to the Instagram

FeedViewController: User selected a Food Model to share
Service: Food Model was posted to the Facebook

在 TypeScript 中使用模式

复杂度: ★★★

流行度: ★☆☆

使用示例: 桥接模式在处理跨平台应用、 支持多种类型的数据库服务器或与多个特定种类 (例如云平台和社交网络等) 的 API 供应商协作时会特别有用。

识别方法: 桥接可以通过一些控制实体及其所依赖的多个不同平台之间的明确区别来进行识别。

概念示例

本例说明了桥接设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

index.ts: 概念示例

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/**
* The Abstraction defines the interface for the "control" part of the two class
* hierarchies. It maintains a reference to an object of the Implementation
* hierarchy and delegates all of the real work to this object.
*/
class Abstraction {
protected implementation: Implementation;

constructor(implementation: Implementation) {
this.implementation = implementation;
}

public operation(): string {
const result = this.implementation.operationImplementation();
return `Abstraction: Base operation with:\n${result}`;
}
}

/**
* You can extend the Abstraction without changing the Implementation classes.
*/
class ExtendedAbstraction extends Abstraction {
public operation(): string {
const result = this.implementation.operationImplementation();
return `ExtendedAbstraction: Extended operation with:\n${result}`;
}
}

/**
* The Implementation defines the interface for all implementation classes. It
* doesn't have to match the Abstraction's interface. In fact, the two
* interfaces can be entirely different. Typically the Implementation interface
* provides only primitive operations, while the Abstraction defines higher-
* level operations based on those primitives.
*/
interface Implementation {
operationImplementation(): string;
}

/**
* Each Concrete Implementation corresponds to a specific platform and
* implements the Implementation interface using that platform's API.
*/
class ConcreteImplementationA implements Implementation {
public operationImplementation(): string {
return 'ConcreteImplementationA: Here\'s the result on the platform A.';
}
}

class ConcreteImplementationB implements Implementation {
public operationImplementation(): string {
return 'ConcreteImplementationB: Here\'s the result on the platform B.';
}
}

/**
* Except for the initialization phase, where an Abstraction object gets linked
* with a specific Implementation object, the client code should only depend on
* the Abstraction class. This way the client code can support any abstraction-
* implementation combination.
*/
function clientCode(abstraction: Abstraction) {
// ..

console.log(abstraction.operation());

// ..
}

/**
* The client code should be able to work with any pre-configured abstraction-
* implementation combination.
*/
let implementation = new ConcreteImplementationA();
let abstraction = new Abstraction(implementation);
clientCode(abstraction);

console.log('');

implementation = new ConcreteImplementationB();
abstraction = new ExtendedAbstraction(implementation);
clientCode(abstraction);/**
* The Abstraction defines the interface for the "control" part of the two class
* hierarchies. It maintains a reference to an object of the Implementation
* hierarchy and delegates all of the real work to this object.
*/
class Abstraction {
protected implementation: Implementation;

constructor(implementation: Implementation) {
this.implementation = implementation;
}

public operation(): string {
const result = this.implementation.operationImplementation();
return `Abstraction: Base operation with:\n${result}`;
}
}

/**
* You can extend the Abstraction without changing the Implementation classes.
*/
class ExtendedAbstraction extends Abstraction {
public operation(): string {
const result = this.implementation.operationImplementation();
return `ExtendedAbstraction: Extended operation with:\n${result}`;
}
}

/**
* The Implementation defines the interface for all implementation classes. It
* doesn't have to match the Abstraction's interface. In fact, the two
* interfaces can be entirely different. Typically the Implementation interface
* provides only primitive operations, while the Abstraction defines higher-
* level operations based on those primitives.
*/
interface Implementation {
operationImplementation(): string;
}

/**
* Each Concrete Implementation corresponds to a specific platform and
* implements the Implementation interface using that platform's API.
*/
class ConcreteImplementationA implements Implementation {
public operationImplementation(): string {
return 'ConcreteImplementationA: Here\'s the result on the platform A.';
}
}

class ConcreteImplementationB implements Implementation {
public operationImplementation(): string {
return 'ConcreteImplementationB: Here\'s the result on the platform B.';
}
}

/**
* Except for the initialization phase, where an Abstraction object gets linked
* with a specific Implementation object, the client code should only depend on
* the Abstraction class. This way the client code can support any abstraction-
* implementation combination.
*/
function clientCode(abstraction: Abstraction) {
// ..

console.log(abstraction.operation());

// ..
}

/**
* The client code should be able to work with any pre-configured abstraction-
* implementation combination.
*/
let implementation = new ConcreteImplementationA();
let abstraction = new Abstraction(implementation);
clientCode(abstraction);

console.log('');

implementation = new ConcreteImplementationB();
abstraction = new ExtendedAbstraction(implementation);
clientCode(abstraction);

Output.txt: 执行结果

1
2
3
4
5
Abstraction: Base operation with:
ConcreteImplementationA: Here's the result on the platform A.

ExtendedAbstraction: Extended operation with:
ConcreteImplementationB: Here's the result on the platform B.

概念示例

假设你有两台电脑: 一台 Mac 和一台 Windows。 还有两台打印机: 爱普生和惠普。 这两台电脑和打印机可能会任意组合使用。 客户端不应去担心如何将打印机连接至计算机的细节问题。

如果引入新的打印机, 我们也不会希望代码量成倍增长。 所以, 我们创建了两个层次结构, 而不是 2x2 组合的四个结构体:

  • 抽象层: 代表计算机
  • 实施层: 代表打印机

这两个层次可通过桥接进行沟通, 其中抽象层 (计算机) 包含对于实施层 (打印机) 的引用。 抽象层和实施层均可独立开发, 不会相互影响。

computer.go: 抽象

1
2
3
4
5
6
package main

type computer interface {
print()
setPrinter(printer)
}

mac.go: 精确抽象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

type mac struct {
printer printer
}

func (m *mac) print() {
fmt.Println("Print request for mac")
m.printer.printFile()
}

func (m *mac) setPrinter(p printer) {
m.printer = p
}

windows.go: 精确抽象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

type windows struct {
printer printer
}

func (w *windows) print() {
fmt.Println("Print request for windows")
w.printer.printFile()
}

func (w *windows) setPrinter(p printer) {
w.printer = p
}

printer.go: 实施

1
2
3
4
5
package main

type printer interface {
printFile()
}

epson.go: 具体实施

1
2
3
4
5
6
7
8
9
10
package main

import "fmt"

type epson struct {
}

func (p *epson) printFile() {
fmt.Println("Printing by a EPSON Printer")
}

hp.go: 具体实施

1
2
3
4
5
6
7
8
9
10
package main

import "fmt"

type hp struct {
}

func (p *hp) printFile() {
fmt.Println("Printing by a HP Printer")
}

main.go: 客户端代码

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
package main

import "fmt"

func main() {

hpPrinter := &hp{}
epsonPrinter := &epson{}

macComputer := &mac{}

macComputer.setPrinter(hpPrinter)
macComputer.print()
fmt.Println()

macComputer.setPrinter(epsonPrinter)
macComputer.print()
fmt.Println()

winComputer := &windows{}

winComputer.setPrinter(hpPrinter)
winComputer.print()
fmt.Println()

winComputer.setPrinter(epsonPrinter)
winComputer.print()
fmt.Println()
}

output.txt: 执行结果

1
2
3
4
5
6
7
8
9
10
Print request for mac
Printing by a HP Printer

Print request for mac
Printing by a EPSON Printer

Print request for windows
Printing by a HP Printer

Print request for windows

根据: Golang By Example