【设计模式】桥接模式(Bridge)
目录
一、问题导入
二、问题剖析
三、结构成分
1.具体组成
2.示例题目
四、代码实现(仅供参考)
五、优劣
1.优势:
2.劣势:
六、个人理解
一、问题导入
手机已经是生活中必不可少的一部分,一个手机有着多种品牌,比如华为、苹果、vivo、oppo等,同时,对于每部手机而言,其都有着相应的系统,比如安卓,ios,鸿蒙等。
要是每出一个新品牌都要单独适配所有系统,或者每更一个新系统都要适配所有品牌,工作量会非常庞大,那么该如何解决呢?
接下来,就让我们去理解桥接模式(Bridge)
二、问题剖析
一种比较直观的思路是为每个手机品牌单独扩展系统,其结构如下:
可以看到,每个品牌的手机都要重复实现对应的系统(比如 Vivo 要做 Vivo+Android、Vivo+IOS,Apple 要做 Apple+Android、Apple+IOS)。当新增一个品牌(如‘小米’)或新增一个系统(如‘鸿蒙’)时,需要新增的实现类数量会成倍增加(新增品牌要适配所有现有系统,新增系统要被所有现有品牌适配)。这种方案不仅冗余,还会导致“类爆炸”,维护成本极高。(实现类的个数为2×2=4,增加一个品牌将变成3×2=6)
我们会发现,Android、IOS 这些系统的核心逻辑是通用的,却被每个品牌重复实现了。为了解决这个问题,桥接模式的核心思想是将“抽象部分(手机品牌)’和‘实现部分(系统)”分离,让它们各自独立变化、自由组合,其结构如下:
在这个结构中,手机品牌和系统彻底解耦:品牌的扩展(新增“小米”)不会影响系统层,系统的更新(新增“鸿蒙”)也不会依赖品牌层。使用时只需通过“组合”的方式将两者关联,极大地减少了冗余代码,提升了扩展性。(实现类的个数为2+2=4,增加一个品牌将变成3+2=5)
三、结构成分
1.具体组成
从问题剖析当中,我们可以抽象出其桥接模式的组成:
(1)抽象类:定义抽象部分的接口(如手机的品牌特性),是对 “是什么” 的高层抽象。同时,它通过组合的方式维护一个对 “实现者” 的引用,形成连接抽象与实现的 “桥”(例如Phone
类持有System
接口的引用)
(2)具体抽象类:继承自抽象类,扩展抽象接口的功能,实现具体的抽象逻辑(例如Vivo
、Apple
作为具体品牌,在Phone
的基础上添加品牌独有的特性)。
(3)实现者:定义实现部分的接口(如系统的核心功能),是对 “怎么做” 的底层抽象,不直接依赖抽象类(例如System
接口定义系统的启动、运行等方法)。
(4)具体实现者:实现 “实现者接口”,提供具体的功能实现(例如Android
、iOS
作为具体系统,实现System
接口的方法)。
2.示例题目
在上图中,实现者是jacket和trouser,而抽象类则是Man和Lady。
实现者(Implementor):聚焦于 “具体怎么做”,定义底层实现的接口(如Jacket
、Trouser
定义了 “如何被穿着” 的具体方法,属于实现维度)。
抽象(Abstraction):聚焦于 “整体做什么”,定义高层抽象的接口(如Man
、Lady
定义了 “穿衣服” 这一整体行为的逻辑,属于抽象维度)。
四、代码实现(仅供参考)
#pragma once#include<iostream>
#include<string>namespace _BridgePattern
{//实现类接口(对应的系统)class System{public:virtual void show()const = 0;};//具体实现类(安卓和ios)class Android : public System{public:void show()const override { std::cout << "Android " << std::endl; }};class IOS : public System{public:void show()const override { std::cout << "IOS " << std::endl; }};//抽象类class Phone{protected:System* m_system;public:Phone(System* system) :m_system(system) {}virtual void show()const = 0;};//扩展抽象类(vivo,苹果)class Vivo : public Phone{public:Vivo(System* system) :Phone(system) {}void show()const override{std::cout << "Vivo: ";m_system->show();}};class Apple : public Phone{public:Apple(System* system) :Phone(system) {}void show()const override{std::cout << "Apple: ";m_system->show();}};void test(){System* android = new Android();System* ios = new IOS();Phone* vivo = new Vivo(android);Phone* apple = new Apple(ios);vivo->show();apple->show();// 释放内存delete vivo;delete apple;delete android;delete ios;}
}
五、优劣
1.优势:
(1)抽象与实现的分离
(手机品牌(Vivo/Apple)和系统(Android/IOS)各自独立修改,比如给 Android 新增 “分屏功能”,无需改动任何品牌类。)
(2)可扩展性极佳
(新增品牌(如小米)只需继承Phone
,新增系统(如鸿蒙)只需实现System
,无需修改现有代码。)
(3)使细节对客户透明
(客户端只需关注 “Vivo+Android” 的组合,无需知道系统内部实现。)
2.劣势:
(1)增加了理解和设计系统的难度
(需要提前拆分 “抽象” 和 “实现” 两个维度,对设计能力有要求(比如初学者可能难以判断哪些是抽象维度,哪些是实现维度))
(2)要求开发者面向抽象进行设计和编程
(开发者需习惯 “面向接口编程”,而非直接依赖具体类,初期上手可能有门槛。)
六、个人理解
桥接模式以抽象与实现分离的根本思想,通过组合的方式实现了一种降维,将原本 n×m 的二维复杂度降到了 n+m 的一维复杂度。这种“分离独立维度、自由组合”的思路非常灵活 —— 比如游戏中,武器(剑 / 弓)与属性特效(火焰 / 寒冰)是两个独立维度,用桥接模式分离后,新增雷电特效只需实现特效接口,新增‘法杖’只需扩展武器类,无需修改现有代码,极大减少了类的数量。