一、结构型设计模式总览
结构型设计模式关注的是如何将对象和类组装成更大的结构,同时保持结构的灵活性和高效性。它们主要用于处理类与对象之间的组合关系,帮助构建更易维护、扩展和复用的系统架构。
七大经典结构型设计模式
| 模式名称 | 核心思想概述 |
|---|---|
| 适配器模式(Adapter) | 提供中间层,让接口不兼容的对象能够协作 |
| 桥接模式(Bridge) | 将抽象与实现分离为两个独立层次,提高灵活性 |
| 组合模式(Composite) | “组合优于继承”,动态组装程序类的功能,常用于树形结构 |
| 装饰器模式(Decorator) | 为对象动态添加新行为,通过封装扩展功能 |
| 外观模式(Facade) | 为复杂系统/库提供简单统一的接口,隐藏内部复杂性 |
| 享元模式(Flyweight) | 共享内存中的繁重部分,通过引用差异部分优化内存占用(高频用于游戏开发) |
| 代理模式(Proxy) | 提供原始对象的替代品或占位符,控制访问并可能对请求进行预处理 |
二、适配器模式(Adapter Pattern)
1. 模式定义与核心目的
- 适配器模式的核心是:提供一个中间层(适配器),让原本接口不兼容的对象能够在一起顺利协作。
- 类比现实:就像电源适配器能让不同规格的插头插入插座一样,软件中的适配器能让不同接口的对象“互通有无”。
2. 游戏开发典型应用场景:多输入设备兼容
场景描述:
在游戏开发中,玩家可以使用多种输入设备操作游戏,例如:
- 键盘(Keyboard)
- 手柄(Gamepad / GamePad)
每种设备有自己独立的输入接口方法:
- 键盘类调用:
pressKey() - 手柄类调用:
pressButton()
但如果游戏逻辑层直接调用这些具体设备的输入方法,就需要为每种设备编写独立且冗余的代码,缺乏统一性,难以维护和扩展。
3. 适配器模式的解决方案
(1)定义统一的目标接口(Target Interface)
class InputDevice {
public:
virtual void input() = 0; // 统一的输入方法
};
- 这是我们期望游戏逻辑层使用的统一接口,简单且与具体设备无关。
- 目标接口通常是先于各种具体设备存在的,或者是在系统需要兼容新设备时重构引入的。
(2)存在多个原接口(Adaptee / 原有设备接口)
- 键盘类:
Keyboard,提供方法pressKey() - 手柄类:
Gamepad,提供方法pressButton()
这些是原有的、接口不兼容的具体实现类,它们不能直接被游戏逻辑层统一调用。
(3)定义适配器类(Adapter)
-
我们为每种输入设备创建一个适配器类:
KeyboardAdapter:继承自InputDevice,内部包含一个Keyboard对象GamepadAdapter:继承自InputDevice,内部包含一个Gamepad对象
-
适配器类的关键作用:
- 在构造函数中保存具体的输入设备对象(如键盘或手柄)
- 实现统一的
input()方法,在方法内部调用具体设备的原始输入方法(如pressKey()或pressButton())
适配器就像一个“翻译官”,把具体设备的“语言”(pressKey / pressButton)翻译成目标接口的“通用语言”(input())。
(4)客户端(游戏逻辑层)调用
- 游戏逻辑层只依赖统一的
InputDevice接口,不再关心具体是键盘还是手柄。 - 在运行时,根据条件创建不同的适配器对象(如键盘适配器或手柄适配器),然后统一调用
input()方法即可。
4. 适配器模式的关键构成要素总结
| 要素 | 说明 | 示例(输入设备场景) |
|---|---|---|
| 目标接口(Target) | 客户端期望使用的统一接口,简单易用 | InputDevice 接口中的 input() 方法 |
| 原接口(Adaptee) | 需要被适配的原有对象接口,通常与目标接口不兼容 | Keyboard::pressKey() 和 Gamepad::pressButton() |
| 适配器(Adapter) | 实现目标接口,内部持有原接口对象,负责将原接口方法转换为统一的目标接口方法 | KeyboardAdapter 和 GamepadAdapter |
通过引入适配器,客户端代码与具体设备实现完全解耦,只需面向统一的接口编程,大大提高了系统的灵活性和可扩展性。
5. 适配器模式的典型应用拓展
除了输入设备的兼容外,适配器模式在游戏及工业开发中还有其他常见应用:
游戏策划表格数据兼容
- 背景:游戏策划使用 CSV、JSON、XML 等格式的策划表格配置游戏数据(任务、技能、剧情等),这些表格可能随着版本迭代发生变化。
- 问题:程序需要读取并解析这些不同格式的数据,而且每种格式的接口和结构可能差异较大。
- 解决方案:为每种数据格式创建一个适配器,统一封装解析逻辑,对外提供一致的接口。这样,无论策划使用哪种格式的表格,程序都能通过适配器无缝读取数据,无需修改核心逻辑。
6. 适配器模式的优缺点分析
优点:
- 遵循单一职责原则:将接口转换的逻辑封装在适配器中,而不是分散在业务代码里。
- 符合开闭原则:当需要支持新的设备或数据格式时,只需添加新的适配器类,而无需修改现有代码。
- 提高系统灵活性与可扩展性:通过统一的接口,客户端代码可以轻松应对不同类型的对象,而无需关心其具体实现。
- 解耦客户端与具体实现:客户端只依赖目标接口,与具体设备或数据源实现完全隔离。
缺点:
- 引入额外类,增加代码复杂度:每增加一种新设备或格式,通常需要新增一个适配器类,可能导致类数量增多,代码层次变深。
- 可能过度设计:如果原接口之间的差异较小,或者可以通过简单抽象(如提取公共基类/接口)解决问题,那么引入适配器可能显得冗余。
- 性能开销(轻微):适配器模式通常涉及一层方法调用转发,在极端性能敏感场景下可能带来微小开销(但对大多数应用包括游戏来说影响极小)。
三、总结
适配器模式核心价值:
通过引入中间适配层,让接口不兼容的对象能够无缝协作,实现代码的解耦、复用与扩展。
适用场景:
- 需要让多个不同接口的对象在同一个系统中协同工作(如多种输入设备、多种数据源)
- 系统需要兼容旧有接口或第三方库,但又希望对外提供统一易用的接口
- 未来可能频繁接入新类型对象,希望避免频繁修改现有代码
实际应用举例:
- 游戏中的键盘、手柄、触摸屏等输入设备统一管理
- 不同格式的策划数据表格(CSV/JSON/XML)解析适配
- 第三方 SDK 或库的接口与项目内部逻辑的对接
1655

被折叠的 条评论
为什么被折叠?



