【设计模式】桥接模式(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)具体抽象类 :继承自抽象类,扩展抽象接口的功能,实现具体的抽象逻辑(例如VivoApple作为具体品牌,在Phone的基础上添加品牌独有的特性)。

(3)实现者 :定义实现部分的接口(如系统的核心功能),是对 "怎么做" 的底层抽象,不直接依赖抽象类(例如System接口定义系统的启动、运行等方法)。

(4)具体实现者 :实现 "实现者接口",提供具体的功能实现(例如AndroidiOS作为具体系统,实现System接口的方法)。

2.示例题目

在上图中,实现者 是jacket和trouser,而抽象类则是Man和Lady。

实现者(Implementor) :聚焦于 "具体怎么做",定义底层实现的接口(如JacketTrouser定义了 "如何被穿着" 的具体方法,属于实现维度)。

抽象(Abstraction) :聚焦于 "整体做什么",定义高层抽象的接口(如ManLady定义了 "穿衣服" 这一整体行为的逻辑,属于抽象维度)。

四、代码实现(仅供参考)

cpp 复制代码
#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 的一维复杂度。这种**"分离独立维度、自由组合"** 的思路非常灵活 ------ 比如游戏中,武器(剑 / 弓)与属性特效(火焰 / 寒冰)是两个独立维度,用桥接模式分离后,新增雷电特效只需实现特效接口,新增'法杖'只需扩展武器类,无需修改现有代码,极大减少了类的数量。