目录
一、问题提出
二、桥接模式的结构与优势
三、桥接模式的实现
四、桥接模式的深入讨论
五、总结
一、问题提出
面向对象设计的核心原则
面向对象设计的核心可以总结为两点:松耦合(Coupling)和高内聚(Cohesion)。面向对象系统的目标是通过提高模块内部的内聚性,降低模块间的耦合性来构建灵活、可维护的系统。然而,在实际开发中,这一目标往往难以实现,开发者常常面临以下典型问题:
(1)初始实现:客户提出需求,开发者使用一个类(A)来实现功能。
(2)算法扩展:需求变化,需要支持两种算法,于是设计调整为抽象基类,并派生出两个具体类(A1 和 A2)来实现不同的算法。
(3)平台扩展:客户要求支持不同操作系统,于是再抽象出一个基类(A0),并为每个操作系统派生具体类(A00 和 A01,其中 A00 是原来的类 A)。此时,系统中已有 4 个类。
(4)需求持续变化:客户又提出新的算法需求,类的数量进一步增加。
(5)类爆炸问题:随着需求的不断变化,类的数量迅速膨胀,系统变得难以维护。
桥接模式正是为了解决这类问题而提出的。
二、桥接模式的结构与优势
模式结构
桥接模式的典型结构如下图所示。该模式将系统分为两个独立的部分:
抽象部分:定义高层业务逻辑。
实现部分:封装具体实现细节。
这两个部分可以独立地进行修改和扩展。例如,当需求变化需要从抽象部分派生新的子类时,不再需要通过继承来添加子类(如上面举例的 A1 和 A2)。同样,当需要添加新的算法时,只需修改实现部分,而无需改动抽象部分。
模式优势
通过桥接模式,系统的扩展变得更加优雅:
抽象与实现分离:抽象部分和实现部分可以独立变化,互不影响。
减少类爆炸:通过组合而非继承,避免了类的数量呈指数级增长。
提高可维护性:系统结构更加清晰,维护成本显著降低。
三、桥接模式的实现
以下是桥接模式的完整代码示例(使用 C++ 实现):
代码片段 1:Abstraction.h
// Abstraction.h
#ifndef _ABSTRACTION_H_ // 防止头文件被多次包含
#define _ABSTRACTION_H_
// 前向声明 AbstractionImp 类,避免头文件依赖
class AbstractionImp;
// 抽象类 Abstraction:定义高层接口
class Abstraction {
public:
virtual ~Abstraction() = default; // 虚析构函数,确保派生类对象能够正确释放资源
virtual void Operation() = 0; // 纯虚函数,定义抽象接口
protected:
Abstraction() = default; // 构造函数,保护权限,防止直接实例化
private:
// 禁用拷贝构造函数和赋值运算符,防止对象复制
Abstraction(const Abstraction&) = delete;
Abstraction& operator=(const Abstraction&) = delete;
};
// 具体抽象类 RefinedAbstraction:继承自 Abstraction,实现高层接口
class RefinedAbstraction : public Abstraction {
public:
explicit RefinedAbstraction(AbstractionImp* imp); // 显式构造函数,接受实现部分的指针
~RefinedAbstraction() override; // 析构函数
void Operation() override; // 实现抽象接口
private:
AbstractionImp* _imp; // 指向实现部分的指针,通过组合方式实现桥接
};
#endif //~_ABSTRACTION_H_
代码片段 2:Abstraction.cpp
// Abstraction.cpp
#include "Abstraction.h"
#include "AbstractionImp.h"
#include <iostream>
using namespace std;
// Abstraction 构造函数和析构函数
Abstraction::Abstraction() = default;
Abstraction::~Abstraction() = default;
// RefinedAbstraction 构造函数
RefinedAbstraction::RefinedAbstraction(AbstractionImp* imp) : _imp(imp) {}
// RefinedAbstraction 析构函数
RefinedAbstraction::~RefinedAbstraction() {
delete _imp; // 释放实现部分的对象
}
// RefinedAbstraction 的 Operation 实现
void RefinedAbstraction::Operation() {
_imp->Operation(); // 委托给实现部分执行具体操作
}
代码片段 3:AbstractionImp.h
// AbstractionImp.h
#ifndef _ABSTRACTIONIMP_H_ // 防止头文件被多次包含
#define _ABSTRACTIONIMP_H_
// 实现类 AbstractionImp:定义具体实现的接口
class AbstractionImp {
public:
virtual ~AbstractionImp() = default; // 虚析构函数,确保派生类对象能够正确释放资源
virtual void Operation() = 0; // 纯虚函数,定义具体实现接口
protected:
AbstractionImp() = default; // 构造函数,保护权限,防止直接实例化
private:
// 禁用拷贝构造函数和赋值运算符,防止对象复制
AbstractionImp(const AbstractionImp&) = delete;
AbstractionImp& operator=(const AbstractionImp&) = delete;
};
// 具体实现类 ConcreteAbstractionImpA
class ConcreteAbstractionImpA : public AbstractionImp {
public:
ConcreteAbstractionImpA() = default; // 构造函数
~ConcreteAbstractionImpA() override = default; // 析构函数
void Operation() override; // 实现具体操作
};
// 具体实现类 ConcreteAbstractionImpB
class ConcreteAbstractionImpB : public AbstractionImp {
public:
ConcreteAbstractionImpB() = default; // 构造函数
~ConcreteAbstractionImpB() override = default; // 析构函数
void Operation() override; // 实现具体操作
};
#endif //~_ABSTRACTIONIMP_H_
代码片段 4:AbstractionImp.cpp
// AbstractionImp.cpp
#include "AbstractionImp.h"
#include <iostream>
using namespace std;
// AbstractionImp 构造函数和析构函数
AbstractionImp::AbstractionImp() = default;
AbstractionImp::~AbstractionImp() = default;
// ConcreteAbstractionImpA 的 Operation 实现
void ConcreteAbstractionImpA::Operation() {
cout << "ConcreteAbstractionImpA...." << endl;
}
// ConcreteAbstractionImpB 的 Operation 实现
void ConcreteAbstractionImpB::Operation() {
cout << "ConcreteAbstractionImpB...." << endl;
}
代码片段 5:main.cpp
// main.cpp
#include "Abstraction.h"
#include "AbstractionImp.h"
#include <iostream>
using namespace std;
int main(int argc, char* argv[]) {
// 创建具体实现对象
AbstractionImp* imp = new ConcreteAbstractionImpA();
// 创建具体抽象对象,并注入实现对象
Abstraction* abs = new RefinedAbstraction(imp);
// 执行操作
abs->Operation();
// 释放资源
delete abs; // abs 的析构函数会释放 imp
return 0;
}
代码说明
桥接模式通过将抽象部分(Abstraction 类)和实现部分(AbstractionImp 类)分离,使得两者可以独立变化。这种设计方式显著降低了系统的耦合性。
四、桥接模式的深入讨论
模式理解难点
桥接模式是设计模式中较为复杂且难以理解的一种,但其在面向对象开发中应用广泛。GoF 在描述桥接模式时指出,该模式“将抽象部分与它的实现部分分离,使得它们可以独立地变化”。这句话看似简单,却容易引发误解。
Bruce Eckel 在《Thinking in Patterns》中提到,桥接模式是 GoF 描述得最不清晰的设计模式之一。主要原因在于“实现”一词的含义。通常情况下,“实现”指的是继承基类并实现其接口,但在桥接模式中,“实现”指的是通过组合(委托)的方式来实现用户需求。理解这一点是掌握桥接模式的关键。
组合优于继承
桥接模式的核心思想是通过组合而非继承来实现功能需求。这正是面向对象设计中的一个重要原则:**优先使用组合而非继承(Favor Composition Over Inheritance)**。继承虽然强大,但容易导致类层次结构复杂化,而组合则提供了更大的灵活性和更低的耦合性。
五、总结
桥接模式通过解耦抽象与实现,有效地解决了面向对象设计中的类爆炸问题。它不仅提高了系统的可维护性,还为系统的扩展提供了更大的灵活性。理解并掌握桥接模式,将使你的设计更加优雅和高效。