当前位置: 首页 > news >正文

精读C++20设计模式——结构型设计模式:外观模式

精读C++20设计模式——结构型设计模式:外观模式

前言

​ ⚠:笔者的这个设计模式谈不上了解,甚至可以说是现场学习的。所以这个部分会有所混杂GPT的生成内容,因此请您谨慎参考!

​ 外观模式简直就是字如其名:我们一切跟着外观走——倒不如说,我们将接口实现分离的策略重新换了一个更好的名称:外观模式(Facade)

没太看懂?GPT是这样打比方的:想象你走进一家大型酒店,前台接待小姐很少把厨房、保洁、安保、工程维护这些后端细节告诉你。你只要对前台说“我要入住”,她帮你办好一切——房卡、叫客服、通知打扫、开启电梯口令。这就是外观模式的直观比喻:对外提供一个简单、统一的接口,把一堆复杂子系统的交互和时序隐藏在幕后,让客户端只关心“我要做什么”,而不用管“怎么做”。

​ 其实这就是我们自然的一个想法——我们完全可以将更多更加低级的模块有机的组合起来,针对调用方的需求组合,我们就产生了客户端特供的表现形式。

​ 笔者认为处于这个角度,外观模式不是一个设计模式而是一个设计的思路——不是发明新能力,而是把已有的能力按职责做有意义的封装:减少依赖、降低耦合、集中处理通用流程与错误边界,从而让上层代码更专注于业务逻辑。

说一个笔者的例子

​ 笔者之前给IMX6ULL开发板有做过一个多媒体播放器。类名有些忘记了,但是就是这样做的:有 NetworkStream 负责拉流、AudioDecoderVideoDecoder 负责解码、Renderer 负责显示、SubtitleEngine 负责加载字幕。最原始的客户端代码可能像这样:

// client.cpp(无外观,直接调用子系统)
NetworkStream stream;
stream.open(url);
auto rawPacket = stream.readPacket();VideoDecoder vdec;
vdec.init(codecParams);
vdec.sendPacket(rawPacket);
vdec.decodeFrame();AudioDecoder adec;
adec.init(codecParams);
adec.sendPacket(rawPacket);
adec.decodeFrame();Renderer renderer;
renderer.setup(window);
renderer.renderFrame(vdec.getFrame(), adec.getFrame());SubtitleEngine sub;
sub.load(subUrl);
sub.sync(renderer.getTimestamp());

​ 你看到了嘛?如果我们直接将这些代码堆放到MainWindows里,这太麻烦了。完全可以把这一系列Sequence做一个抽象,把资源托管,异常处理和协调工作的部分组合起来。


第一改:引入简单门面(单一职责的入口)

​ 笔者意识到这个问题之后,立马封装了一个新的类,这里咱们就在讲设计模式,就叫做 MediaPlayerFacade吧!它的职责是:接受一个播放请求,协调子系统按正确顺序工作,并在出错时负责清理与报告。客户端只需要一行代码就能播放视频:player.play(url)

// MediaPlayerFacade.h
class MediaPlayerFacade {
public:MediaPlayerFacade();~MediaPlayerFacade();bool play(const std::string& url); // 简单接口:成功/失败void stop();private:std::unique_ptr<NetworkStream> stream_;std::unique_ptr<VideoDecoder> vdec_;std::unique_ptr<AudioDecoder> adec_;std::unique_ptr<Renderer> renderer_;std::unique_ptr<SubtitleEngine> subtitle_;
};

实现中,play() 会负责打开流、初始化解码器、循环读包并驱动渲染。任何子系统细节(如何重试、缓冲策略、错误映射)都封装在门面里。客户端调用变得简单且稳定:

MediaPlayerFacade player;
if (!player.play("https://example.com/stream.m3u8")) {std::cerr << "播放失败\n";
}

​ 现在我们惊喜的发现,我们直接拿到了一个媒体播放器模块了,可以随意的按照我们的想法进行调整,而不用自己手动调整下面的一堆子模块的协作!


第二改:把异常、资源与日志放到门面里

门面最有价值的地方之一是能统一处理错误和资源生命周期。把初始化失败的清理逻辑放在门面中,客户端无需关心。示例实现(伪代码与关键片段):

bool MediaPlayerFacade::play(const std::string& url) {try {stream_ = std::make_unique<NetworkStream>();stream_->open(url);auto vidParams = stream_->getVideoParams();vdec_ = std::make_unique<VideoDecoder>();if (!vdec_->init(vidParams)) throw std::runtime_error("vdec init failed");auto audParams = stream_->getAudioParams();adec_ = std::make_unique<AudioDecoder>();if (!adec_->init(audParams)) throw std::runtime_error("adec init failed");renderer_ = std::make_unique<Renderer>();renderer_->setup(window_);// 主循环(简化)while (running_) {auto pkt = stream_->readPacket();if (pkt.isVideo()) vdec_->sendPacket(pkt);if (pkt.isAudio()) adec_->sendPacket(pkt);auto vfrm = vdec_->getFrameIfReady();auto afrm = adec_->getFrameIfReady();if (vfrm || afrm) renderer_->renderFrame(vfrm, afrm);}return true;} catch (const std::exception& e) {LOG_ERROR("播放失败: {}", e.what());stop(); // 统一清理return false;}
}

​ 现在,我们还让它具备了异常处理的能力了!后面,笔者也做了异步处理和通知处理,但是这就离我们的话题有点远了,感兴趣的朋友可自行研究。


何时使用外观(以及何时别用它)

外观最适合用在“你需要把复杂子系统的协作逻辑封装成单一入口”的场景:例如启动/关闭一组服务、媒体播放、数据库连接池的统一管理、复杂构建流程(编译器的前端构建 API)等。优点在于减少客户端与细节耦合、集中处理错误和生命周期、便于修改底层而不干扰使用者。

但如果你滥用外观,把所有细节都全包进一个“大门面”,门面很容易变成“上帝对象(God Object)”:它积累太多逻辑,把原本应该由子模块负责的职责都吞下,导致单元测试变得困难,代码也更难维护。外观应该是“门面”,不是“重写子系统的替代品”;它应组织调用与抽象流程,而不是重新实现业务逻辑。

http://www.dtcms.com/a/423202.html

相关文章:

  • How Can Objects Help Video-Language Understanding?论文阅读
  • 《AI智能体实战开发教程(从0到企业级项目落地)》全网上线|CSDN B站同步首发
  • Python学习历程——组织结构(包含for、if、while等等)
  • cronet从编译到修改之: 支持IP直连
  • 美团网站建设规划书俄罗斯搜索引擎入口 yandex
  • Java微服务知识点详细总结
  • 做网站需要哪些工程师网络优化岗位详细介绍
  • 南昌网站全新开发小型教育网站的开发建设开题报告
  • .NET开发中3秒判断该用 IEnumerable 还是 IQueryable
  • 【Java EE进阶 --- SpringBoot】Mybatis操作数据库(基础)
  • 【Docker + DockerCompose】安装步骤+演示
  • TLS全流程 + Nginx HTTPS配置实战 + 会话绑定 vs 复制的架构选型
  • cms搭建网站剪辑素材网站免费
  • Qt Widgets 应用程序核心类 - QApplication 详解
  • 电商类网站开发项目书app安装下载
  • S7-200 SMART 开放式用户通信(OUC)深度指南:TCP/ISO-on-TCP编程(下)
  • 华为云在工业软件上云上的优势
  • C++ 并发编程与多线程面试题精选
  • 【2025年9月版 亲测可用】《人民日报》PDF文件下载
  • 企业网站建设记什么会计科目php wap网站源码
  • 深圳企业网站建设价格怎么创建微信公众号免费
  • 使用IOT-Tree Server通过PPI协议连接西门子PLC S7-200 Smart
  • 潮汐流量处理系统设计方案
  • 鸿蒙与iOS跨平台开发方案全解析
  • 把项目通过pycharm上传到github(两种方式)
  • 邢台网站推广专业服务正规电商平台有哪些
  • 适配多元场景物料搬运!IXTUR气控永磁铁为多行业注入自动化新动能
  • 以自然语言实现AI自动化Browser-use 详细介绍与使用指南
  • 怎么使用创客贴网站做图h5网站开发
  • 青岛网站设计品牌企业Wordpress启动mysql