桥 接 模 式

news2025/6/2 12:23:28

在玩游戏的时候我们常常会遇到这样的机制:我们可以随意选择不同的角色,搭配不同的武器。这时只有一个抽象上下文的策略模式就不那么适用了,因为一旦我们使用继承的方式,武器和角色总有一方会变得难以扩展。这时,我们就需要通过组合来连接二者,这种连接的设计模式叫做桥接模式(Bridge)。


简介

定义:将抽象与实现部分分离,使它们都可以独立地变化。是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式。

使用场景:对象的结构可以分为多个维度,而且每个维度需要能够独立变化和扩展。

那么问题来了,何谓“抽象”与“实现”的分离呢?

抽象(Abstraction):指隐藏复杂的内部细节,仅对外暴露必要的接口或功能的做法。C++中通常通过三种方式实现。(1)抽象类:就是包含纯虚函数的类,没有办法被实例化,仅仅定义接口。(2)接口:在C++里的接口就是通过纯虚函数类模拟的。(3)封装:通过私有域和保护域隐藏数据,公共域提供访问方法。

实现(Implementation):实现就是抽象的具体化。因为”抽象“中的接口还只是一个规范,没有具体的行为,所以”实现“就是实现接口背后实际的代码逻辑。

还记得策略模式中的抽象策略类和具体策略类吗?

/*抽象策略类*/
class AttackStrategy {
public:
virtual ~AttackStrategy(){}
//重击接口
virtual void thump() = 0;
//技能接口
virtual void skill() = 0;
};
/*具体策略类*/
//劈棍
class PiGun : public AttackStrategy {
public:
void thump() override {
		cout << "腾空而起,高举棍子,向你劈去。" << endl;
	}
void skill() override {
		cout << "破棍式!斩棍式!" << endl;
	}
	~PiGun() {};
};

没错,策略模式通过“继承”的方式将抽象与实现分离了。为了让各种实现逻辑灵活切换,策略模式没有把实现的逻辑封装在策略类中,而是将“实现”分离出此类,而类本身通过提供一个接口让“抽象”与“实现”通过继承的关系联系起来。

而策略模式作为行为型模式,注重的是行为的动态替换,并不在意“抽象”层次的变化。而桥接模式作为关注抽象与实现都能独立变化的结构型模式,它在分离抽象与实现时不通过继承关系关联二者,而是通过组合关系。

通俗的说,我如果是法师职业的“角色”,其实只是我用法杖这个“武器”有加成,我也可以用剑的(近战法师)。那么“用法杖攻击”和“用剑攻击”的行为就是”实现“。我的法师职业或替换为战士职业的“角色"就是提供接口的”抽象“。为了我可以随意搭配,我就需要把抽象和实现分离,再通过组合关联起来(可以看下文的代码理解)。

桥接模式有以下4个关键角色:

1、抽象:代表着“抽象”层次,定义抽象接口,持有“实现”层次的引用或指针。

2、扩展抽象:“扩展”意为对抽象的细化,比如添加角色的专属逻辑需要,是更具的的抽象,仍然是抽象层次的一部分。通常是抽象类的子类或具体实现类。

3、实现:代表着“实现”层次,定义实现接口。

4、具体实现:提供实现的具体逻辑。

但话又说回来,抽象接口和实现接口又是什么?简单来说他们分别是抽象和实现层次的一部分,抽象接口顾名思义就是抽象的,它不需要实现具体的功能,只需要把操作委托给实现接口就好了。也就是说抽象接口是依赖于实现接口的,从而解耦变化维度。

class Character {  //抽象接口
protected:
    Weapon* weapon;  //实现接口(的指针)
public:
    //这里暂时省略扩展抽象
    void fight() const { 
        weapon->use();  //操作委托给实现接口
    }
};

​​​​​​​实现

那么现在就用C++代码模拟上文中我们说到的角色与武器搭配,演示一下传说中的桥接模式罢!

首先我们先要确定,“角色”和“武器”这两个维度谁来做抽象层还有谁来做实现层。这时,我们就要区分谁适合做结构的高层或者控制层。仔细一想,武器是决定行为方式的,角色是持有武器的,那理所应当角色可以适合充当控制层,即抽象维度为“角色”,实现维度为“武器”。

姑且设定角色可以有战士和法师,武器有剑和法杖。他们分别作为子类充当扩展抽象角色以及具体实现角色。理清结构之后就可以开始写下代码了。

#include <iostream>
using namespace std;
//把实现层次放在抽象层次的前面,因为抽象需要用到实现的方法
/*----实现层次----*/
/*实现*/
class Weapon {
public:
virtual ~Weapon() {}
//提供使用武器的接口(实现接口)
virtual void use() const = 0;
};
/*具体实现*/
//剑
class Sword : public Weapon {
public:
void use() const override {
		cout << "接招吧,雷霆半月斩!" << endl;
	}
};
//法杖
class Staff : public Weapon {
public:
void use() const override {
		cout << "接招吧,圣灵魔闪光!" << endl;
	}
};
/*----抽象层次----*/
/*抽象*/
class Character {
//设置指针为保护成员,这样一来派生类可以直接访问实现层
protected:
//此处封装“实现”的指针,这是桥接的核心,即组合
	Weapon* weapon;
public:
//explicit抑制类型转换关键字
explicit Character(Weapon* weapon) : weapon(weapon){}
virtual ~Character() {
delete weapon;//释放桥接的指针
	}
//提供战斗的接口(抽象接口)
virtual void fight() const = 0;
};
/*扩展抽象(即具体角色)*/
//战士
class Warrior : public Character {
public:
//委托构造
explicit Warrior(Weapon* weapon) : Character(weapon) {}
void fight() const override {
		cout << "哈哈哈,吾乃帝国名扬四海的狂战士:";
		weapon->use();
	}
};
//法师
class Mage : public Character {
public:
//委托构造
explicit Mage(Weapon* weapon) : Character(weapon) {}
void fight() const override {
		cout << "哈哈哈,吾乃教会首屈一指的魔法使:";
		weapon->use();
	}
};
int main() {
//新建角色
	Character* c = new Warrior(new Sword);
	c->fight();
delete c;
	c = new Mage(new Sword);
	c->fight();
delete c;
	c = new Warrior(new Staff);
	c->fight();
delete c;
	c = new Mage(new Staff);
	c->fight();
delete c;	
return 0;
}

​​​​​​​当然,如果我们遇到多个维度而非上述两个维度的情况,可以考虑使用“嵌套桥接”。

class Character {
protected:
	Weapon* weapon;
//防具指针
	Armor* armor;
public:
//explicit抑制类型转换关键字
explicit Character(Weapon* weapon, Armor* armor) : weapon(weapon), armor(armor){}
virtual ~Character() {
delete weapon;//释放桥接的指针
delete armor;
	}
virtual void fight() const = 0;
};
class Warrior : public Character {
public:
explicit Warrior(Weapon* weapon) : Character(weapon) {}
void fight() const override {
		cout << "哈哈哈,吾乃帝国名扬四海的狂战士:";
		weapon->use();
		armor->defend();
	}
};

​​​​​​


小结

总的来说,桥接模式的关联关系建立在抽象层。所以我们在扩展维度时,可能需要针对抽象进行设计与编程。但其在游戏开发中适用于许多场景,也同样符合开闭原则与单一职责原则,应用十分广泛。

如有补充纠正欢迎留言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2394078.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于 Flink+Paimon+Hologres 搭建淘天集团湖仓一体数据链路

摘要&#xff1a;本文整理自淘天集团高级数据开发工程师朱奥老师在 Flink Forward Asia 2024 流式湖仓论坛的分享。内容主要为以下五部分&#xff1a; 1、项目背景 2、核心策略 3、解决方案 4、项目价值 5、未来计划 01、项目背景 1.1 当前实时数仓架构 当前的淘天实时架构是从…

多杆合一驱动城市空间治理智慧化

引言&#xff1a;城市“杆林困境”与智慧化破局 走在现代城市的街道上&#xff0c;路灯、监控、交通信号灯、5G基站等杆体林立&#xff0c;不仅侵占公共空间&#xff0c;更暴露了城市治理的碎片化问题。如何让这些“沉默的钢铁”升级为城市的“智慧神经元”&#xff1f;答案在…

用QT写一个车速表

主要包含以下绘制步骤&#xff1a; 1、绘制画布&#xff1a; /** 绘制画布 */ void Widget::initCanvas(QPainter &painter) {//消除锯齿painter.setRenderHint(QPainter::Antialiasing,true);//设置底色painter.setBrush(QColor(0,0,0));painter.drawRect(rect());//平移…

数控技术应用理实一体化平台VR实训系统

::产品概述:: 目前我国本科类院校学生普遍存在的问题就是缺少对实际工作的了解&#xff0c;一直在学习相关专业的理论知识&#xff0c;对社会的相关企业的用人情况不了解。这也就直接导致了毕业的学生和社会上的用人单位需求有点脱节&#xff0c;这也是由于我国的现行本科教育侧…

C# 将HTML文档、HTML字符串转换为图片

在.NET开发中&#xff0c;将HTML内容转换为图片的需求广泛存在于报告生成、邮件内容存档、网页快照等场景。Free Spire.Doc for .NET作为一款免费的专业文档处理库&#xff0c;无需Microsoft Word依赖&#xff0c;即可轻松实现这一功能。本文将深入解析HTML文档和字符串转图片两…

界面控件DevExpress WinForms v24.2新版亮点:富文本编辑器功能全新升级

DevExpress WinForms拥有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForms能完美构建流畅、美观且易于使用的应用程序&#xff0c;无论是Office风格的界面&#xff0c;还是分析处理大批量的业务数据&#xff0c;它都能轻松胜…

华为云Flexus+DeepSeek征文|华为云 Flexus X 加速 Dify 平台落地:高性能、低成本、强可靠性的云上选择

目录 前言 1 一键部署 Dify 平台的完整步骤 1.1 选择模板 1.2 参数配置 1.3 资源栈设置 1.4 配置确认与部署 2 Flexus X 服务器的技术优势 2.1 柔性算力随心配 2.2 一直加速一直快 2.3 越用越省降本多 2.4 安全可靠更放心 3 Flexus X 在 Dify 解决方案中的性能体验…

Jenkins 2.479.1安装和邮箱配置教程

1.安装 在JDK安装并设置环境变量完成后&#xff0c;下载官网对应的war版本&#xff0c;在对应目录下打开命令行窗口并输入 java -jar jenkins.war其余参数感兴趣可以自行查阅&#xff0c;这里启动的 jenkins 服务默认占用8080端口&#xff0c;在浏览器输入 localhost:8080进入…

DFS入门刷题c++

目录 821. 跳台阶 - AcWing题库 ​92. 递归实现指数型枚举 - AcWing题库 ​P1706 全排列问题 - 洛谷 (luogu.com.cn) P1157 组合的输出 - 洛谷 (luogu.com.cn) ​P1036 [NOIP 2002 普及组] 选数 - 洛谷 (luogu.com.cn) P2089 烤鸡 - 洛谷 (luogu.com.cn) P1088 [NOIP 2…

ToolsSet之:十六进制及二进制编辑运算工具

ToolsSet是微软商店中的一款包含数十种实用工具数百种细分功能的工具集合应用&#xff0c;应用基本功能介绍可以查看以下文章&#xff1a; Windows应用ToolsSet介绍https://blog.csdn.net/BinField/article/details/145898264 ToolsSet中Number菜单下的Hex Operate工具可以进…

【Python训练营打卡】day40 @浙大疏锦行

DAY 40 训练和测试的规范写法 知识点回顾&#xff1a; 1. 彩色和灰度图片测试和训练的规范写法&#xff1a;封装在函数中 2. 展平操作&#xff1a;除第一个维度batchsize外全部展平 3. dropout操作&#xff1a;训练阶段随机丢弃神经元&#xff0c;测试阶段eval模式关闭dropo…

MCP Server的五种主流架构:从原理到实践的深度解析

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 在AI大模型与外部数据交互的浪潮中&#xff0c;MCP Server&#xff08;Model Context Protocol Server&#xff09;已成为连接模型与现实世界的桥梁。本文…

跨协议协同智造新实践:DeviceNet-EtherCAT网关驱动汽车焊接装配效能跃迁

在汽车制造领域&#xff0c;机器人协作对于提升生产效率与产品质量至关重要。焊接、装配等关键环节&#xff0c;需要机器人与各类设备紧密配合。JH-DVN-ECT疆鸿智能的devicenet从站转ethercat主站协议网关&#xff0c;成为实现这一高效协作的得力助手&#xff0c;尤其是在连接欧…

让 Deepseek 写一个尺码计算器

下面是一个简单的尺码计算器微信小程序的代码实现&#xff0c;包含页面布局、逻辑处理和样式。 1. 项目结构 size-calculator/ ├── pages/ │ ├── index/ │ │ ├── index.js │ │ ├── index.json │ │ ├── index.wxml │ │ └── inde…

代码随想录算法训练营第60期第五十三天打卡

大家好&#xff0c;我们今天来到了最后一章图论&#xff0c;其实图论比较难&#xff0c;涉及的算法也比较多&#xff0c;今天比较重要的就是深度优先搜索与广度优先搜索&#xff0c;后面的迪杰斯特拉算法等算法在我们求最短路都会涉及到&#xff0c;还有最近公共祖先&#xff0…

Nacos实战——动态 IP 黑名单过滤

1、需求分析 一些恶意用户&#xff08;‏可能是黑客、爬虫、DDoS ؜攻击者&#xff09;可能频繁请求服务器资​源&#xff0c;导致资源占用过高。针对这种问题&#xff0c;可以通过IP‏ 封禁&#xff0c;可以有效拉؜黑攻击者&#xff0c;防止资源​被滥用&#xff0c;保障合法…

实验设计与分析(第6版,Montgomery)第5章析因设计引导5.7节思考题5.14 R语言解题

本文是实验设计与分析&#xff08;第6版&#xff0c;Montgomery著&#xff0c;傅珏生译) 第5章析因设计引导5.7节思考题5.14 R语言解题。主要涉及方差分析&#xff0c;正态假设检验&#xff0c;残差分析&#xff0c;交互作用图。 dataframe<-data.frame( strengthc(9.60,9.…

在Ubuntu20.04上安装ROS Noetic

本章教程,主要记录在Ubuntu20.04上安装ROS Noetic。 一、添加软件源 sudo sh -c . /etc/lsb-release && echo "deb http://mirrors.tuna.tsinghua.edu.cn/ros/ubuntu/ `lsb_release -cs` main" > /etc/apt/sources.list.d/ros-latest.list二、设置秘钥 …

python里面导入yfinance的时候报错

我的代码&#xff1a; import yfinance as yf import os proxy http://127.0.0.1:7890 # 代理设置&#xff0c;此处修改 os.environ[HTTP_PROXY] proxy os.environ[HTTPS_PROXY] proxydata yf.download("AAPL",start"2010-1-1",end"2021-8-1&quo…

winform LiveCharts2的使用--图表的使用

介绍 对于图标&#xff0c;需要使用到livechart2中的CartesianChart 控件&#xff0c;是一个“即用型”控件&#xff0c;用于使用笛卡尔坐标系创建绘图。需要将Series属性分配一组ICartesianSeries。 例如下面代码&#xff0c;创建一个最简单的图表&#xff1a; cartesianCha…