桥接(Bridge)模式

桥接模式是一种设计模式,用于将抽象部分与其实现部分分离,以便它们可以独立变化。这种模式提高了代码的灵活性,允许在运行时选择不同的实现。文章详细解释了桥接模式的意图、动机、适用性、结构和参与者,并提供了代码示例和实际应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

桥接(Bridge)模式

隶属类别——对象结构型模式


1. 意图

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

2. 别名

Handle/Body

3. 动机

当一个抽象可能有多个实现时,通常用继承来协调它们。抽象类定义对该抽象的接口。而具体的子类则用不同方式加以实现。但是此方法有时不够灵活,继承机制将抽象部分与它的实现部分固定在一起,使得难以对抽象部分和实现部分独立地进行修改,扩充和重用。

让我们考虑在一个用户界面工具箱中,一个可移植的Window抽象部分的实现。例如,这一抽象部分应该允许用户开发一些在X Window System和IBM的Presentation Manager(PM)系统中都可以使用的应用程序。运用继承机制,我们可以定义Window抽象类和它的两个子类XWindow 与PMWindow,由它们分别实现不同系统平台的Windows界面,但是继承机制有两个不足之处:

    1. 扩展Window抽象使之适用于不同种类的窗口或新的系统平台很不方便。假设有Window的一个子类IconWindow,它专门将Window抽象用于图标处理。为了使IconWindows支持两个系统平台,我们必须实现两个新类XIconWindow和PMIconWindow,更为糟糕的是,我们不得不为每一种类型的窗口都定义两个类。而为了支持第三个系统平台我们还必须为每一种窗口定义一个新的Window子类,如下图所示:

在这里插入图片描述

    1. 继承机制使得客户代码与平台相关。每当客户创建一个窗口,必须要实例化一个具体的类,这个类有特定的实现部分。例如,创建XWindow对象会将Window抽象与X Window的实现部分绑定起来,这使得客户程序依赖于X Window的实现部分。这将使得很难将客户代码移植到其他平台上去。

客户在创建窗口时应该不涉及到其具体实现部分。仅仅是窗口的实现部分依赖于应用运行的平台。这样客户代码在创建窗口时就不应涉及到特定的平台。

Bridge模式解决了以上问题的方法是,将Window抽象和它的实现部分分别放在地理的类层结构中。其中一个类层次结构针对窗口接口(Window,IconWindow,TransientWindow),另外一个独立的类层次结构针对平台相关的窗口实现部分,这个类层次结构的根类为WindowsImpl。例如XWindosImp提供一个基于X Window系统的实现,如图所示:

在这里插入图片描述

对Window子类的所有操作都是用WindowImp接口中的抽象操作实现的,这就将窗口的抽象与系统的相关实现分离开来(单一责任原则)。因此,我们1将Window与WindowImp之间的关系称为桥接,因为它在抽象类与它的实现之间起到了桥梁作用,这使他们可以独立地变化,通过委托的方式来进行方法调用,这使得调用更加灵活。

4. 适用性

以下一些情况可以使用Bridge模式:

  • 你不希望在抽象和它实现部分之间有一个固定的绑定关系。例如这种情况可能是因为,在程序运行时刻实现部分应可以被选择或者切换。
  • 类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充,这时Bridge模式使你可以对不同的抽象接口和实现部分进行组合,并对它们进行扩充。
  • 对一个抽象的实现部分的修改应对客户不产生影响,即客户的代码不必重新编译。
  • 你想对客户完全隐藏抽象的实现部分。
  • 正如动机第一节的第一个类图所示的那样,有许多类要生成。这样一种类层次结构说明你必须将对象分解成两个部分。Rumbaugn称这种类层次结构为"嵌套的普化"(nested generalizations)
  • 你想在多个对象间共享实现(可能使用引用计数),但同时要求客户并知道这一点,一个简单的例子便是Coplien的String类,在这个类中多个对象可以共享同一个字符串表示(StringRep)

5. 结构

在这里插入图片描述

6. 参与者

  • Abstraction(Window)
    • 定义抽象类的接口
    • 维护一个指向Implementor类型对象的指针
  • RefinedAbstraction(IconWindow)
    • 扩充有Abstraction定义的接口
  • Implementor(WindowImp)
    • 定义实现类的接口,该接口不一定要与Abstraction的接口完全一致,事实上这两个接口可以完全不同。一般来讲,Implementor接口仅提供基本操作,而Abstraction则定义了基于这些操作的较高层次的操作。
  • ConcreteImplementor(XWindowImp,PMWindowImp)
    • 实现Implementor接口并定义它的具体实现

7. 协作

  • Abstraction将client的请求转发给它的Implementor对象。

8. 效果

Bridge模式有以下一些优点:

  • 1)分离接口及其实现部分 一个实现未必不变地绑定在一个接口上。抽象类的实现可以在运行时刻进行配置,一个对象甚至可以在运行时刻改变它的实现。

    将Abstraction和Implementor分离有助于降低对实现部分编译时刻的依赖性,当改变一个实现类是,并不需要重新编译Abstraction类和它的客户程序。为了保证一个类库的不同版本之间的二进制兼容性,一定要有这个性质。

    另外,接口与实现分离有助于分层,从而产生更好的结构化系统,系统的高层部分仅需知道Abastraction和Implementor即可。

    1. 提供可扩充性 你可以独立地对Abstraction和Implementor层次结构进行扩充。
    1. 实现细节对客户透明 你可以对客户隐藏实现细节,例如共享Implementor对象以及共享的引用计数机制(如果有的话)

Bridge模式的缺点:

  • 增加了复杂度

9. 实现

使用Bridge模式需要注意以下一些问题:

    1. 仅有一个Implementor 在仅有一个实现的时候,没有必要创建一个抽象的Implememtor类。这是Bridge模式的退化情况,在Abstraction与Implementor之间有一种一对一的关系。尽管如此,当你希望改变一个类的实现不会影响到已有的客户程序时,模式的分离机制还是非常有用的——也就是说,不必重新编译它们,仅需重新连接即可。

    在Java仅需Implementor引用设置为private。

    1. 创建正确的Implementor对象 当存在多个Implementor类的时候,你应该用何种方法,在何时何处确定创建一个哪一个Implement类呢?

    如果Abstraction知道所有的ConcreteImplementor类,它就可以在它的构造器中对其中的一个类进行实例化,它可以通过传递给构造器的参数确定实例化哪一个类。例如,如果一个collection类支持多重实现,就可以根据collection的大小决定实例化哪一个类。链表的实现可以用于较小的collection类,而hash表则可用于就较大的collection类。

    另外一个方法是首先选择一个缺省的实现,然而根据需要改变这个实现。例如,如果一个collection的大小超过了一定的阈值时,它将会切换它的实现,使之更适用于表目较多的collection。

    也可以代理给另一个对象,由它一次决定。在Window/WindowImp的例子中,我们可以引入一个factory对象,该对象的唯一职责就是封装系统平台的细节。这个对象知道如何应该为所用的平台创建何种类型的WindowImpl对象;Window仅需向它请求一个WindowImp,而它会返回正确的WindowImp对象。这种方法的优点是Abstraction类不和任何一个Implementor类直接耦合。

    1. 共享Implementor对象 在Java中如何共享Implementor对象????
    1. 采用多重继承机制 在C++ 可以使用多重继承机制将抽象接口和它的实现部分结合起来,例如,一个类可以用Public 方式继承Abstraction和private方式继承ConcreteImplementor。但是由于这种方法依赖于静态继承,它将实现部分与接口固定不变的绑定在一起,因此不可能使用多重继承的方法实现真正的Bridge模式——至少C++不行,Java更不行,Java连多重继承都无法实现。

10. 代码示例

首先创建Abstraction——Weapon.java

ppublic interface Weapon {
	void wield();
	void swing();
	void retract();
	Enchantment getEnchantment();
}

接下来是Implementor——Enchantment.java

public interface Enchantment {
	void activate();
	void apply();
	void deactivate();
}

接着是ConcreteImplementor——FlyEnchantment.java & SoulEatingEnchantment.java

FlyEnchantment.java

public class FlyingEnchantment implements Enchantment{
	
	@Override
	public void activate() {
		System.out.println("The item begins to glow faintly.");
	}
	
	@Override
	public void apply() {
		System.out.println("The item flies and strikes the enemies finally returning to owner's hand.");
	}
	
	@Override
	public void deactivate() {
		System.out.println("The item's glow fades.");
	}
}

SoulEnchantment.java

public class SoulEatingEnchantment implements Enchantment {
	
	@Override
	public void activate() {
		System.out.println("The item spreads bloodlust.");
	}
	
	@Override
	public void apply() {
		System.out.println("The item eats the soul of enemies");
	}
	
	@Override
	public void deactivate() {
		System.out.println("Bloodlust slowly disappears.");
	}
}

然后是RefinedAbstraction——Sword.java & Dragger.java

Sword.java

public class Sword implements Weapon {
	private final Enchantment enchantment;
	
	public Sword(Enchantment enchantment) {
		this.enchantment = enchantment;
	}
	
	@Override
	public void wield() {
		System.out.println("The sword is wielded");
		enchantment.activate();
	}
	
	@Override
	public void swing() {
		System.out.println("The sword is swinged");
		enchantment.apply();
	}
	
	@Override
	public void retract() {
		System.out.println("The sword is retracted");
		enchantment.deactivate();
	}
	
	@Override
	public Enchantment getEnchantment() {
		return enchantment;
	}
}

Dagger.java

public class Dagger implements Weapon {
	private final Enchantment enchantment;
	
	public Dagger(Enchantment enchantment) {
		this.enchantment = enchantment;
	}
	
	@Override
	public void wield() {
		System.out.println("The Dagger is wielded");
	    enchantment.activate();
	}
	
	@Override
	public void swing() {
		System.out.println("The Dagger is swinged");
		enchantment.apply();
	}
	
	@Override
	public void retract() {
		System.out.println("The Dagger is retracted");
		enchantment.deactivate();
	}
	
	@Override
	public Enchantment getEnchantment() {
		return enchantment;
	}
}

接下是Clent——WeaponShop.java

public class WeaponShop {
	public static void main(String[] args) {
		Weapon sword = new Sword(new SoulEatingEnchantment());
		sword.wield();
		sword.swing();
		sword.retract();
		
		System.out.println();
		
		Weapon dagger = new Dagger(new FlyingEnchantment());
		dagger.wield();
		dagger.swing();
		dagger.retract();
	}
}

以及对于的测试结果

The sword is wielded
The item spreads bloodlust.
The sword is swinged
The item eats the soul of enemies
The sword is retracted
Bloodlust slowly disappears.

The Dagger is wielded
The item begins to glow faintly.
The Dagger is swinged
The item flies and strikes the enemies finally returning to owner's hand.
The Dagger is retracted
The item's glow fades.

最后附上类的UML图(PS:由于这里Abstraction是接口,在RefinedAbstraction中持有Implementor引用)

在这里插入图片描述

11. 已知应用

NeXT’s AppKit 在图像生成和显示中使用了Bridge模式,一个图像可以有不同的表示方式,一个图像的最佳显式方式取决于显式设备的特性,特别是它的色彩数目和分辨率。如果没有AppKit的帮助,每一个应用程序中应用开发者都要确定在不同的情况下使用哪一种实现方法。

12. 相关应用

  • Abstracat Factory Abstract Factory模式可以用来创建和配置一个特定的Bridge模式。
  • Adapter Adapter模式用来帮助无关的类协同合作,它通常在系统设计完成后才会被使用。然而,Bridge模式则是在系统开始时就被使用,它使得抽象接口和实现部分可以独立进行改变。

13. 设计原则口袋

  • 封装变化
  • 为交互对象的松耦合设计而努力
  • 针对接口编程,不针对实现编程
  • 多用组合,少用继承
  • 类应该对扩展开放,对修改关闭
  • 依赖抽象,不依赖具体类
  • 只和密友交交谈
  • 好莱坞原则——别找我,我会找你
  • 单一责任原则——类应该只有一个改变的理由

14. 参考文献

《设计模式:可复用面向对象软件的基础》

《HeadFirst设计模式》

### 如何在 VMware 中配置和使用桥接网络模式 #### 安装与权限设置 为了确保能够成功配置桥接模式,在启动 VMware 之前应以管理员身份运行该程序。这一步骤能防止因权限不足而导致的配置失败[^2]。 #### 虚拟网络编辑器中的设置 通过菜单栏选择`编辑->虚拟网络编辑器`来访问网络选项。在此界面下可以看到多个用于不同类型的网络连接的适配器,其中 `VMnet0` 对应于桥接模式下的虚拟交换机。确认此接口已启用并与物理网卡关联,从而允许虚拟环境内的设备如同处于同一局域网中一样工作[^1]。 #### 卸载并重新安装协议 如果遇到无法切换至桥接模式的问题,则可能需要先卸载名为 "VMware Bridge Protocol" 的组件再尝试重启软件。完成上述操作之后再次进入虚拟网络编辑器调整为所需的桥接状态即可解决临时性的功能异常情况。 #### 验证MAC地址获取 一旦完成了以上步骤,进入到具体的Linux发行版实例内部(例如Ubuntu),可以通过命令行工具查询当前系统的硬件信息,特别是关注网卡所对应的 MAC 地址是否正确无误地被识别出来。对于 Ubuntu 来说,可以利用 `ip addr` 命令来进行这项检查工作[^3]。 ```bash ip addr show ``` #### 测试连通性 最后一步是要验证新建立起来的网络链接是否有效运作。可以在宿主机操作系统里试着 ping 一下刚刚获得 IP 地址的目标机器;反过来也是一样——从客户 OS 发起同样的请求回传给外部世界。成功的响应意味着整个过程顺利完成,并且现在应该能够在两者之间自由传输数据包了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值