新谈设计模式 Chapter 06 — 适配器模式 Adapter

张开发
2026/4/6 3:35:23 15 分钟阅读

分享文章

新谈设计模式 Chapter 06 — 适配器模式 Adapter
Chapter 06 — 适配器模式 Adapter灵魂速记转接头——圆孔插方头接口不兼容我来转。秒懂类比你带了一个美标插头的笔记本去欧洲。欧洲墙上是圆孔美标是扁头。你不会把墙壁的插座拆了重装也不会把笔记本的电源线剪了——你买一个转接头。适配器就是这个转接头把一个类的接口转换成客户端期望的另一个接口。问题引入// 你的代码用的是这个接口classMediaPlayer{public:virtualvoidplay(conststd::stringfilename)0;};// 但第三方库提供的是这个——接口完全不同classThirdPartyVideoLib{public:voidloadVideo(conststd::stringpath){/* ... */}voidstartPlayback(){/* ... */}};// 你不能改第三方库的代码也不想改自己的接口// 怎么办—— 适配器模式结构客户端期望的接口 适配器 被适配的类 ┌──────────┐ ┌───────────┐ ┌──────────────────┐ │ Target │ │ Adapter │ │ Adaptee │ ├──────────┤ ├───────────┤ ├──────────────────┤ │ request()│◄────│ request()│───────→│ specificRequest()│ └──────────┘ └───────────┘ └──────────────────┘ 内部调用 Adaptee 的方法 转换成 Target 的接口C 实现对象适配器组合方式推荐#includeiostream#includememory#includestring// 目标接口客户端期望的 classMediaPlayer{public:virtual~MediaPlayer()default;virtualvoidplay(conststd::stringfilename)0;};// 被适配者第三方库不能改 classAdvancedVideoPlayer{public:voidloadFile(conststd::stringpath){std::cout [高级播放器] 加载文件: path\n;}voidinitCodec(){std::cout [高级播放器] 初始化编解码器\n;}voidstartRender(){std::cout [高级播放器] 开始渲染播放\n;}};// 适配器把 AdvancedVideoPlayer 包装成 MediaPlayer classVideoPlayerAdapter:publicMediaPlayer{public:VideoPlayerAdapter():player_(std::make_uniqueAdvancedVideoPlayer()){}voidplay(conststd::stringfilename)override{// 把一个简单接口调用翻译成多步复杂调用player_-loadFile(filename);player_-initCodec();player_-startRender();}private:std::unique_ptrAdvancedVideoPlayerplayer_;// 组合不是继承};// 客户端 voidclientCode(MediaPlayerplayer,conststd::stringfile){std::cout播放 file:\n;player.play(file);// 客户端只知道 MediaPlayer 接口}intmain(){VideoPlayerAdapter adapter;clientCode(adapter,movie.mp4);clientCode(adapter,lecture.avi);}输出播放 movie.mp4: [高级播放器] 加载文件: movie.mp4 [高级播放器] 初始化编解码器 [高级播放器] 开始渲染播放 播放 lecture.avi: [高级播放器] 加载文件: lecture.avi [高级播放器] 初始化编解码器 [高级播放器] 开始渲染播放类适配器多重继承方式// 同时继承 Targetpublic和 Adapteeprivate// private 继承表示用 Adaptee 的实现但不暴露它的接口classVideoPlayerAdapter:publicMediaPlayer,privateAdvancedVideoPlayer{public:voidplay(conststd::stringfilename)override{loadFile(filename);// 直接调用 Adaptee 的方法因为是继承initCodec();startRender();}};对象适配器用组合类适配器用多重继承。C 是少数支持多重继承的语言所以类适配器在 C 里可行但一般还是推荐对象适配器——组合更灵活耦合更松。什么时候用✅ 适合❌ 别用集成第三方库接口不兼容接口本来就兼容旧系统改造不敢动老代码可以直接修改被适配类想复用一个类但接口不匹配只是想加功能那是装饰器防混淆Adapter vs DecoratorAdapterDecorator目的转换接口增强功能接口变化输入输出接口不同输入输出接口相同被包装的类接口不兼容的类接口相同的类类比电源转接头手机壳加功能但还是手机一句话分清Adapter 换壳Decorator 加料。Adapter vs FacadeAdapterFacade包装几个类通常包装1 个包装一堆目的接口转换简化接口方向让旧接口适配新接口给复杂子系统提供简单入口Adapter vs ProxyAdapterProxy接口不同 → 转换相同 → 代理目的兼容性控制访问现代 C 小贴士有时候用 Lambda 做轻量适配更简洁#includefunctional// 目标接口usingComparatorstd::functionbool(int,int);// 被适配的函数参数顺序相反boollegacyCompare(intb,inta){returnab;}// Lambda 适配Comparator adapted[](inta,intb){returnlegacyCompare(b,a);};STL 中的std::stack、std::queue就是适配器——它们把deque的接口适配成栈/队列的接口。

更多文章