Bridge桥接模式

Bridge桥接模式



前言

关于桥接模式我有以下几个问题想问:

  • 什么是桥接模式?
  • 为什么需要桥接模式(也就是什么情况下使用桥接模式)?
  • 桥接模式的优点是什么?
  • 桥接模式的缺点是什么?

桥接模式与装饰模式很相似,然而,桥接模式主要是为了应对“多维度变化”。同时,桥接模式的使用场景存在一定的局限性。

模式定义

在《设计模式》一书中,对桥模式做了如下释义:

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

GoF这句话的意思是,桥模式能够让抽象部分与实现部分独立变化。这显然是一个解耦的过程,同时,需要两个维度的存在(抽象部分和实现部分)——这是因为,只有存在多个维度时,才有每个维度都可以独立变化的需求。

与继承比较

现在,假设我们需要做一个QQ这样的软件,它包括登陆,发送消息,画图与播放音乐这些功能。因此,抽象基类可以设计成如下样子:

class QQ {
public:
	virtual void login()=0;
	virtual void sendmsg()=0;

	virtual void music()=0;
	virtual void draw()=0;

	virtual ~QQ(){};
};

众所周知,QQ能够安装在win上,也可以在苹果电脑上安装。基于不同设备,它们在播放音乐、画图功能上存在实现差别是理所当然的(例如不同浏览器对css的支持不尽相同)。所以,不同平台,不同实现:

class WinQQ: public QQ {
public:
	virtual void music() {
		// win播放音乐的操作
	}
	virtual void draw() {
		// win画图的操作
	}
};

class AppleQQ: public QQ {
	virtual void music() {
		// apple播放音乐的操作
	}
	virtual void draw() {
		// apple画图的操作
	}
};

依照现实情况,腾讯旗下的QQ其实有两种,一种是完整版的QQ(把它叫作 QQPerfect 吧),另一种是精简版的TIM。或许在登陆过程中,QQPerfect需要播放音乐而TIM为了足够简洁则不允许播放音乐;在发送消息上,QQPerfect能够发送用户自己画出来的图(draw()产生的数据),TIM则不可以。两种版本的QQ都应该得到win和apple平台的支持。

class WinQQPerfect: WinQQ {
public:
	virtual void login() {
		WinQQ::music();
		// 登陆逻辑
	}
	virtual void sendmsg() {
		...
		if (...) {
			WinQQ::draw();
			// 发送逻辑
		}
	}
};

class WinTim: WinQQ {
public:
	virtual void login() {
		// 登陆逻辑
	}
	virtual void sendmsg() {
		// 发送逻辑
	}
};

...  // 省略AppleQQPerfect,AppleTim的代码

事实上,WinQQPerfect与AppleQQPerfect,以及WinTim与AppleTim,无非是平台不同,以至于继承的父类不同(前者继承WinQQ,后者继承AppleQQ),我们可以利用组合的方式合并这些类。此时需要对QQ这个抽象基类做些改动:

class QQ {  // 改动后的QQ
protected:
	QQ* qq;
public:
	QQ(QQ* qq): qq(qq) {}
	virtual void login()=0;
	virtual void sendmsg()=0;

	virtual void music()=0;
	virtual void draw()=0;

	virtual ~QQ(){};
};

class QQPerfect: public QQ {  // 合并后的QQPerfect
public:
	QQPerfect(QQ* qq): QQ(qq) {}
	virtual void login() {
		qq->music();
		// 登陆逻辑
	}
	virtual void sendmsg() {
		...
		if (...) {
			qq->draw();
			// 发送逻辑
		}
	}	
};

...  // 省略TIM的代码

合并WinQQPerfect与AppleQQPerfect之后,麻烦也随之而来,这是因为如果现在实例化一个win版本的QQPerfect,需要这样:

WinQQ wqq = new WinQQ();  // 基类不可以被实例化
QQPerfect wqpft = new QQPerfect(wqq);

可是不行,因为WinQQ继承自抽象基类QQ,而QQ包含了登陆、发送消息、音乐、画图四个纯需函数,WinQQ仅仅重写了music()draw()。也就是说,它还继承了父类的login()sendmsg()两个纯需函数,WinQQ现在是一个抽象基类,不可以被实例化。

那我们在QQ类中实现登陆、发送消息两个功能可以吗?显然不行,因为完整版的QQPerfect与TIM的登陆细节不同。对WinQQ来说,亦是同样道理。

由此,陷入一种僵局状态…

桥接模式

回到最初的起点,整理一下思路。基类QQ有四个纯需函数:登陆,发送,音乐,画图。现在我们有两个需求,一个平台相关(Win、Apple),一个业务相关(QQPerfect、TIM)。画图音乐是平台相关,登陆发送是业务相关,平台与业务是两个维度,可我们将其设计在一个类中,看上去这不合理。

桥接模式认为,它们应该分手。原有的QQ于是一分为二,一个专注业务叫QQ,一个专注平台叫QQPlatform:

class QQ {
protected:
	QQPlatform* platform;
public:
	QQ(QQPlatform* plt): platform(plt);
	virtual void login()=0;
	virtual void sendmsg()=0;
};

class QQPlatform {
public:
	virtual void music()=0;
	virtual void draw()=0;
};

这样一来,平台基于QQPlatform:

class WinQQ: public QQPlatform {
public:
	virtual void music() {
		// win播放音乐的操作
	}
	virtual void draw() {
		// win画图的操作
	}
};

class AppleQQ: public QQPlatform {
	virtual void music() {
		// apple播放音乐的操作
	}
	virtual void draw() {
		// apple画图的操作
	}
};

业务基于QQ:

class QQPerfect: public QQ {
public:
	QQPerfect(QQPlatform* plt): QQ(plt) {}
	virtual void login() {
		plt->music();
		// 登陆逻辑
	}
	virtual void sendmsg() {
		...
		if (...) {
			plt->draw();
			// 发送逻辑
		}
	}
};

class Tim: public QQ {
public:
	QQPerfect(QQPlatform* plt): QQ(plt) {}
	virtual void login() {
		// 登陆逻辑
	}
	virtual void sendmsg() {
		// 发送逻辑
	}
}

现在,平台与业务可以随意组合了:

// win上的完整版QQ
WinQQ* wqq = new WinQQ();
QQPerfect* wqpft = new QQPerfect(wqq);

// apple上的TIM
AppleQQ* aqq = new AppleQQ();
Tim* tim = new Time(aqq);

...

也许不论是完整的QQ还是TIM,其中的登陆逻辑与发送逻辑应该相同,区别在于login()sendmsg()中的其他部分(放音乐或是发送画图数据)。虽说如此,但我仍没有直接在QQ中实现,这里把QQ作为一个抽象基类,是为了告诉子类们:“请按接口设计重写方法”。

关系梳理

继承关系:
在这里插入图片描述

桥接模式:
在这里插入图片描述

能够看到,当平台与业务很多时,桥模式可以有效控制子类的增长速度。同时,注意桥接模式与装饰模式的区别。

模式结构

桥接模式包含如下角色:

  • Abstraction:抽象类
  • RefinedAbstraction:扩充抽象类
  • Implementor:实现类接口
  • ConcreteImplementor:具体实现类

在这里插入图片描述

总结

桥接模式优点:

  • 分离抽象接口与实现部分,保证多维度下每个维度能够独立变化;
  • 桥接模式是对多继承方案的优化,提高代码复用性。

桥接模式缺点:

  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。

感谢

  • 参考李建忠老师的《C++模式设计》
  • 参考《Graphic Design Patterns》中的桥接模式
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值