一、桥模式-结构型
1.1、核心思想
解耦抽象与实现,通俗一点就是将抽象部分与实现部分分离,使它们都可以独立变化。
1.2、为什么会有桥模式?
在软件开发中,由于某些固有的实现逻辑,使得它们具有两个或者更多维度的变化,如果只是使用继承,会造成子类急剧增加,并且子类之间的代码重复性很高 ,桥模式就是为了解决这种问题。
以DNF--地下城与勇士为例,两个维度分别为职业 和武器
在没有使用桥模式之前
cpp
// 角色基类
class Character
{
public:
virtual void attack() = 0;
virtual void showInfo() = 0;
};
// 鬼泣 装备 太刀
class GhostCryWithTanto: public Character
{
public:
void attack() override
{
this->showInfo();
std::cout << "使用技能:鬼斩!" << std::endl;
}
void showInfo() override
{
std::cout << "我是鬼剑士,职业是鬼泣,装备的是太刀" << std::endl;
}
};
// 剑魂 装备 光剑
class SwordSoulWithLightSword: public Character
{
public:
void attack() override
{
this->showInfo();
std::cout << "使用技能:里·鬼剑术!" << std::endl;
}
void showInfo() override
{
std::cout << "我是鬼剑士,职业是剑魂,装备的是光剑" << std::endl;
}
};
// 狂战士 装备 巨剑
class BerserkerWithGreatSword: public Character
{
public:
void attack() override
{
this->showInfo();
std::cout << "使用技能:崩山击!" << std::endl;
}
void showInfo() override
{
std::cout << "我是鬼剑士,职业是狂战士,装备的是巨剑" << std::endl;
}
};
// 阿修罗 装备 短剑
class AsuraWithShortSword: public Character
{
public:
void attack() override
{
this->showInfo();
std::cout << "使用技能:杀意波动!" << std::endl;
}
void showInfo() override
{
std::cout << "我是鬼剑士,职业是阿修罗,装备的是短剑" << std::endl;
}
};
// 漫游 装备 左轮手枪
class WanderWithRevolver: public Character
{
public:
void attack() override
{
this->showInfo();
std::cout << "使用技能:爆头一击!" << std::endl;
}
void showInfo() override
{
std::cout << "我是神枪手,职业是漫游,装备的是左轮手枪" << std::endl;
}
};
// 枪炮师 装备 手炮
class GunslingerWithHandgun: public Character
{
public:
void attack() override
{
this->showInfo();
std::cout << "使用技能:量子爆弹!" << std::endl;
}
void showInfo() override
{
std::cout << "我是神枪手,职业是枪炮师,装备的是手炮" << std::endl;
}
};
// 机械师 装备 自动手枪
class MechanicWithPistol: public Character
{
public:
void attack() override
{
this->showInfo();
std::cout << "使用技能:R-78追击者!" << std::endl;
}
void showInfo() override
{
std::cout << "我是神枪手,职业是机械师,装备的是自动手枪" << std::endl;
}
};
// 弹药专家 装备 步枪
class BullpupWithRifle: public Character
{
public:
void attack() override
{
this->showInfo();
std::cout << "使用技能:尼尔狙击!" << std::endl;
}
void showInfo() override
{
std::cout << "我是神枪手,职业是弹药专家,装备的是步枪" << std::endl;
}
}
// 测试使用
void test1()
{
std::cout<<"=== 不使用桥模式的DNF(职业 + 武器)==="<<std::endl;
// 创建角色
std::vector<Character*> characters;
characters.push_back(new GhostCryWithTanto());
characters.push_back(new SwordSoulWithLightSword());
characters.push_back(new BerserkerWithGreatSword());
characters.push_back(new AsuraWithShortSword());
characters.push_back(new WanderWithRevolver());
characters.push_back(new GunslingerWithHandgun());
characters.push_back(new MechanicWithPistol());
characters.push_back(new BullpupWithRifle());
// 角色攻击
for(const auto& character: characters)
{
character->attack();
}
for(const auto& character: characters){
delete character;
}
}
类数量进行分析:
| 基础职业 | 转职职业 | 武器 | 需要的类数量 |
|---|---|---|---|
| 鬼剑士 | 5个 | 太刀,光剑,巨剑,短剑,钝器 | 除剑魂能拿光剑,其余都不行; 16 + 5 = 21 |
| 神枪手 | 5个 | 左轮枪,手炮,步枪,手弩,自动手枪 | 25 |
问题所在
- 类爆炸:好家伙,目前就两个基础职业,就诞生了 21 + 25 = 46个类
- 维护成本高:比如修改其中一个职业(漫游)的攻击逻辑,需要修改5个类
- 无法动态切换武器:角色在战斗中无法更换武器
- 代码重复:相同的武器逻辑在不同职业中重复
使用桥模式之后
将武器进行抽象 + 职业进行抽象,变成两个维度
- 武器部分进行抽象
c++
class Weapon
{
public:
virtual ~Weapon() = default;
// 武器属性
virtual std::string getName() const = 0;
virtual int getPhysicalAttack() const = 0;
virtual int getMagicalAttack() const = 0;
virtual float getAttackSpeed() const = 0;
virtual int getRequiredLevel() const = 0;
virtual void specialEffect() const = 0;
virtual std::string getSkillEnhancement() const = 0;
virtual Weapon* clone() const = 0;
};
// 太刀
class Katana : public Weapon
{
private:
std::string name;
int level;
public:
Katana(std::string name = "流光星陨刀", int level = 60)
: name(name)
, level(level)
{
}
std::string getName() const override { return name; }
int getPhysicalAttack() const override { return level * 15; }
int getMagicalAttack() const override { return level * 25; }
float getAttackSpeed() const override { return 1.2f; }
int getRequiredLevel() const override { return level; }
void specialEffect() const override
{
std::cout << "太刀特效:出血几率+30%,攻击时有一定的几率附加暗属性伤害\n";
}
std::string getSkillEnhancement()const override
{
return "技能[鬼斩]等级+3,[月光斩]等级+2";
}
Weapon* clone()const override
{
return new Katana(*this);
}
};
// 光剑
class LightSword : public Weapon
{
private:
std::string name;
int level;
public:
LightSword(std::string name = "赤光剑路易斯纳斯", int level = 90)
: name(name)
, level(level)
{
}
std::string getName() const override { return name; }
int getPhysicalAttack() const override { return level * 23; }
int getMagicalAttack() const override { return level * 12; }
float getAttackSpeed() const override { return 2.0f; }
int getRequiredLevel() const override { return level; }
void specialEffect() const override
{
std::cout << "光剑特效:感电几率+30%,攻击时有一定的几率附加光属性伤害\n";
}
std::string getSkillEnhancement()const override
{
return "技能[里·鬼剑术]等级+5,[破军升龙击]等级+2";
}
Weapon* clone()const override
{
return new LightSword(*this);
}
};
// 短剑
class ShortSword : public Weapon
{
private:
std::string name;
int level;
public:
ShortSword(std::string name = "无影剑-艾雷诺", int level = 70)
: name(name), level(level) {
}
std::string getName() const override { return name; }
int getPhysicalAttack() const override { return level * 12; }
int getMagicalAttack() const override { return level * 23; }
float getAttackSpeed() const override { return 0.8f; }
int getRequiredLevel() const override { return level; }
void specialEffect() const override {
std::cout << "短剑特效:增加施放速度+5%, 物理/魔法暴击+2%" << std::endl;
}
std::string getSkillEnhancement() const override {
return "技能[邪光斩]等级+3,[杀意波动]等级+2";
}
Weapon* clone() const override {
return new ShortSword(*this);
}
};
// 巨剑
class GreatSword : public Weapon
{
private:
std::string name;
int level;
public:
GreatSword(std::string name = "魔剑-阿波菲斯", int level = 70)
: name(name), level(level) {
}
std::string getName() const override { return name; }
int getPhysicalAttack() const override { return level * 25; }
int getMagicalAttack() const override { return level * 5; }
float getAttackSpeed() const override { return 0.5f; }
int getRequiredLevel() const override { return level; }
void specialEffect() const override {
std::cout << "巨剑特效:攻击时附加10%额外伤害" << std::endl;
}
std::string getSkillEnhancement() const override {
return "技能[崩山裂地斩]等级+2,[血之狂暴]等级+1";
}
Weapon* clone() const override {
return new GreatSword(*this);
}
};
// 钝器
class BluntWeapon : public Weapon
{
private:
std::string name;
int level;
public:
BluntWeapon(std::string name = "梁月的钝剑", int level = 85)
: name(name), level(level) {
}
std::string getName() const override { return name; }
int getPhysicalAttack() const override { return level * 23; }
int getMagicalAttack() const override { return level * 2; }
float getAttackSpeed() const override { return 0.5f; }
int getRequiredLevel() const override { return level; }
void specialEffect() const override {
std::cout << "钝器特效:攻击时削减敌人25%防御力,每2秒叠加4%攻速、移速" << std::endl;
}
std::string getSkillEnhancement() const override {
return "48级与85级技能等级+2";
}
Weapon* clone() const override {
return new BluntWeapon(*this);
}
};
- 职业部分进行抽象
c++
class Character
{
protected:
std::string name;
int level;
std::string career;
Weapon* weapon; // 桥模式的关键:持有武器的引用
public:
Character(std::string _name, int _level, std::string _career)
: name(_name)
, level(_level)
, career(_career)
, weapon(nullptr)
{
}
virtual ~Character()
{
if (weapon) {
delete weapon;
weapon = nullptr;
}
}
// 装备武器(桥接的关键方法)
void equipWeapon(Weapon* newWeapon) {
if (newWeapon->getRequiredLevel() > level) {
std::cout << "等级不足,无法装备 " << newWeapon->getName()
<< "(需要等级" << newWeapon->getRequiredLevel() << ")" << std::endl;
return;
}
delete weapon; // 卸下旧武器
weapon = newWeapon->clone(); // 装备新武器(深拷贝)
std::cout << name << " 装备了 " << weapon->getName() << std::endl;
}
// 卸下武器
void unequipWeapon() {
if (weapon) {
std::cout << name << " 卸下了 " << weapon->getName() << std::endl;
delete weapon;
weapon = nullptr;
}
}
// 攻击(具体职业实现)
virtual void attack() const = 0;
// 使用职业技能
virtual void useSkill(std::string skillName) const = 0;
// 显示角色信息
virtual void showStatus() const {
std::cout << "\n=== " << name << " ===" << std::endl;
std::cout << "职业:" << career << " Lv." << level << std::endl;
if (weapon) {
std::cout << "武器:" << weapon->getName()
<< "(物攻:" << weapon->getPhysicalAttack()
<< ",魔攻:" << weapon->getMagicalAttack()
<< ",攻速:" << weapon->getAttackSpeed() << ")" << std::endl;
std::cout << "武器特效:";
weapon->specialEffect();
std::cout << "技能增强:" << weapon->getSkillEnhancement() << std::endl;
}
else {
std::cout << "武器:无" << std::endl;
}
}
// 计算总攻击力(职业特性 + 武器)
virtual int getTotalPhysicalAttack() const {
int baseAttack = level * 10; // 基础攻击力
return baseAttack + (weapon ? weapon->getPhysicalAttack() : 0);
}
virtual int getTotalMagicalAttack() const {
int baseAttack = level * 8; // 基础魔法攻击力
return baseAttack + (weapon ? weapon->getMagicalAttack() : 0);
}
};
class GhostSwordMan : public Character
{
public:
GhostSwordMan(std::string name, int level)
: Character(name, level, "鬼剑士") {
}
void attack() const override {
if (weapon) {
std::cout << name << " 使用 " << weapon->getName()
<< " 进行斩击!" << std::endl;
std::cout << " 造成 " << getTotalPhysicalAttack() << " 点物理伤害" << std::endl;
}
else {
std::cout << name << " 使用拳头攻击!" << std::endl;
}
}
void useSkill(std::string skillName) const override {
if (skillName == "鬼斩") {
std::cout << name << " 释放鬼斩!" << std::endl;
std::cout << " 造成 " << getTotalPhysicalAttack() * 2 << " 点暗属性伤害" << std::endl;
}
else if (skillName == "崩山击") {
std::cout << name << " 使用崩山击!" << std::endl;
std::cout << " 造成范围伤害,并击倒敌人" << std::endl;
}
else {
std::cout << name << " 不会这个技能" << std::endl;
}
}
};
还可以再进一步细分五个转职职业:剑魂,狂战士,鬼泣,阿修罗,剑影;针对不同的职业,属性点不一样,专属技能也不同。
c++
void test2()
{
std::cout << "====================桥模式====================" << std::endl;
// 创建武器
Katana* katana = new Katana("西岚的妖刀", 75);
LightSword* lightSword = new LightSword("永恒星河", 65);
ShortSword* shortSword = new ShortSword("巴恩的短剑", 90);
GreatSword* greatSword = new GreatSword("神之意向", 100);
BluntWeapon* bluntWeapon = new BluntWeapon("梁月的钝剑", 100);
// 创建角色
GhostSwordMan* ghostSwordMan = new GhostSwordMan("梁月", 90);
// 装备武器
std::cout << "---------装备武器---------\n";
ghostSwordMan->equipWeapon(katana);
// 使用技能
std::cout << "---------使用技能---------\n";
ghostSwordMan->useSkill("鬼斩");
// 攻击
std::cout << "---------攻击---------\n";
ghostSwordMan->attack();
// 显示状态
std::cout << "---------显示状态---------\n";
ghostSwordMan->showStatus();
// 动态更换武器
std::cout << "---------切换武器---------\n";
ghostSwordMan->unequipWeapon();
ghostSwordMan->equipWeapon(lightSword);
// 再次显示状态
std::cout << "---------再次显示状态---------\n";
ghostSwordMan->showStatus();
// clean
delete ghostSwordMan;
delete katana;
delete lightSword;
delete shortSword;
delete greatSword;
delete bluntWeapon;
}


可以明显看到:
| 特点 | 桥模式 | 不使用桥模式 |
|---|---|---|
| 类数量 | 相对较少 | 类数量较多 |
| 新增职业 | 只需要创建一个新职业类 | 需要为每个武器创建新类 |
| 新增武器 | 只需要创建一个新武器类 | 需要为每个职业创建新类 |
| 动态切换武器 | 可以 | 不能 |
| 扩展性 | 好 | 差 |
| 维护性 | 好 | 差 |
二、总结
2.1、优点:
- 抽象和实现分离,增强系统扩展性:桥接模式将抽象部分与实现部分分离,使得它们可以独立变化。抽象部分和实现部分都可以独立扩展,而不需要修改其他部分。
- 符合开闭原则:通过桥接模式,可以在不修改原有代码的情况下,增加新的抽象层和实现层,从而实现系统的扩展。
- 符合合成复用原则:桥接模式通过将抽象部分和实现部分分离,使得抽象部分和实现部分可以独立变化,从而实现系统的复用。
2.2、缺点:
- 增加了系统的理解与设计难度:桥接模式将抽象部分和实现部分分离,使得系统更加复杂,增加了系统的理解与设计难度。
- 需要正确识别出系统中独立变化的维度:桥接模式要求正确识别出系统中独立变化的维度,否则可能会导致系统设计不合理,增加系统的复杂度。
2.3、使用场景:
- 系统需要在抽象和实现之间增加更多的灵活性
- 系统不希望使用继承导致系统类的个数急剧增加
- 当存在多个独立变化的维度,且每个维度都有可能需要扩展时