5. 结构型模式 - 外观模式

亦称: Facade

 意图

外观模式是一种结构型设计模式, 能为程序库、 框架或其他复杂类提供一个简单的接口

外观设计模式

 问题

假设你必须在代码中使用某个复杂的库或框架中的众多对象。 正常情况下, 你需要负责所有对象的初始化工作、 管理其依赖关系并按正确的顺序执行方法等。

最终, 程序中类的业务逻辑将与第三方类的实现细节紧密耦合, 使得理解和维护代码的工作很难进行。

 解决方案

外观类为包含许多活动部件的复杂子系统提供一个简单的接口。 与直接调用子系统相比, 外观提供的功能可能比较有限, 但它却包含了客户端真正关心的功能。

如果你的程序需要与包含几十种功能的复杂库整合, 但只需使用其中非常少的功能, 那么使用外观模式会非常方便,

例如, 上传猫咪搞笑短视频到社交媒体网站的应用可能会用到专业的视频转换库, 但它只需使用一个包含 encode­(filename, format)方法 (以文件名与文件格式为参数进行编码的方法) 的类即可。 在创建这个类并将其连接到视频转换库后, 你就拥有了自己的第一个外观。

 真实世界类比

电话购物的示例

电话购物。

当你通过电话给商店下达订单时, 接线员就是该商店的所有服务和部门的外观。 接线员为你提供了一个同购物系统、 支付网关和各种送货服务进行互动的简单语音接口。

 外观模式结构

外观设计模式的结构

  1. 外观 (Facade) 提供了一种访问特定子系统功能的便捷方式, 其了解如何重定向客户端请求, 知晓如何操作一切活动部件。

  2. 创建附加外观 (Additional Facade) 类可以避免多种不相关的功能污染单一外观, 使其变成又一个复杂结构。 客户端和其他外观都可使用附加外观。

  3. 复杂子系统 (Complex Subsystem) 由数十个不同对象构成。 如果要用这些对象完成有意义的工作, 你必须深入了解子系统的实现细节, 比如按照正确顺序初始化对象和为其提供正确格式的数据。

    子系统类不会意识到外观的存在, 它们在系统内运作并且相互之间可直接进行交互。

  4. 客户端 (Client) 使用外观代替对子系统对象的直接调用。

 伪代码

在本例中, 外观模式简化了客户端与复杂视频转换框架之间的交互。

外观模式示例的结构

使用单个外观类隔离多重依赖的示例

你可以创建一个封装所需功能并隐藏其他代码的外观类, 从而无需使全部代码直接与数十个框架类进行交互。 该结构还能将未来框架升级或更换所造成的影响最小化, 因为你只需修改程序中外观方法的实现即可。

// 这里有复杂第三方视频转换框架中的一些类。我们不知晓其中的代码,因此无法
// 对其进行简化。class VideoFile
// ……class OggCompressionCodec
// ……class MPEG4CompressionCodec
// ……class CodecFactory
// ……class BitrateReader
// ……class AudioMixer
// ……// 为了将框架的复杂性隐藏在一个简单接口背后,我们创建了一个外观类。它是在
// 功能性和简洁性之间做出的权衡。
class VideoConverter ismethod convert(filename, format):File isfile = new VideoFile(filename)sourceCodec = (new CodecFactory).extract(file)if (format == "mp4")destinationCodec = new MPEG4CompressionCodec()elsedestinationCodec = new OggCompressionCodec()buffer = BitrateReader.read(filename, sourceCodec)result = BitrateReader.convert(buffer, destinationCodec)result = (new AudioMixer()).fix(result)return new File(result)// 应用程序的类并不依赖于复杂框架中成千上万的类。同样,如果你决定更换框架,
// 那只需重写外观类即可。
class Application ismethod main() isconvertor = new VideoConverter()mp4 = convertor.convert("funny-cats-video.ogg", "mp4")mp4.save()

 外观模式适合应用场景

 如果你需要一个指向复杂子系统的直接接口, 且该接口的功能有限, 则可以使用外观模式。

 子系统通常会随着时间的推进变得越来越复杂。 即便是应用了设计模式, 通常你也会创建更多的类。 尽管在多种情形中子系统可能是更灵活或易于复用的, 但其所需的配置和样板代码数量将会增长得更快。 为了解决这个问题, 外观将会提供指向子系统中最常用功能的快捷方式, 能够满足客户端的大部分需求。

 如果需要将子系统组织为多层结构, 可以使用外观。

 创建外观来定义子系统中各层次的入口。 你可以要求子系统仅使用外观来进行交互, 以减少子系统之间的耦合。

让我们回到视频转换框架的例子。 该框架可以拆分为两个层次: 音频相关和视频相关。 你可以为每个层次创建一个外观, 然后要求各层的类必须通过这些外观进行交互。 这种方式看上去与中介者模式非常相似。

 实现方式

  1. 考虑能否在现有子系统的基础上提供一个更简单的接口。 如果该接口能让客户端代码独立于众多子系统类, 那么你的方向就是正确的。

  2. 在一个新的外观类中声明并实现该接口。 外观应将客户端代码的调用重定向到子系统中的相应对象处。 如果客户端代码没有对子系统进行初始化, 也没有对其后续生命周期进行管理, 那么外观必须完成此类工作。

  3. 如果要充分发挥这一模式的优势, 你必须确保所有客户端代码仅通过外观来与子系统进行交互。 此后客户端代码将不会受到任何由子系统代码修改而造成的影响, 比如子系统升级后, 你只需修改外观中的代码即可。

  4. 如果外观变得过于臃肿, 你可以考虑将其部分行为抽取为一个新的专用外观类。

 外观模式优缺点

  •  你可以让自己的代码独立于复杂子系统。
  •  外观可能成为与程序中所有类都耦合的上帝对象。

 与其他模式的关系

  • ​ 外观模式为现有对象定义了一个新接口, 适配器模式则会试图运用已有的接口。 适配器通常只封装一个对象, 外观通常会作用于整个对象子系统上。 当只需对客户端代码隐藏子系统创建对象的方式时, 你可以使用抽象工厂模式来代替外观。 享元模式展示了如何生成大量的小型对象, 外观则展示了如何用一个对象来代表整个子系统。 外观和中介者模式的职责类似: 它们都尝试在大量紧密耦合的类中组织起合作。 外观为子系统中的所有对象定义了一个简单接口, 但是它不提供任何新功能。 子系统本身不会意识到外观的存在。 子系统中的对象可以直接进行交流。 中介者将系统中组件的沟通行为中心化。 各组件只知道中介者对象, 无法直接相互交流。 外观类通常可以转换为单例模式类, 因为在大部分情况下一个外观对象就足够了。 外观与代理模式的相似之处在于它们都缓存了一个复杂实体并自行对其进行初始化。 代理与其服务对象遵循同一接口, 使得自己和服务对象可以互换, 在这一点上它与外观不同。 ​

 代码示例

/*** The Subsystem can accept requests either from the facade or client directly.* In any case, to the Subsystem, the Facade is yet another client, and it's not* a part of the Subsystem.*/
class Subsystem1 {public:std::string Operation1() const {return "Subsystem1: Ready!\n";}// ...std::string OperationN() const {return "Subsystem1: Go!\n";}
};
/*** Some facades can work with multiple subsystems at the same time.*/
class Subsystem2 {public:std::string Operation1() const {return "Subsystem2: Get ready!\n";}// ...std::string OperationZ() const {return "Subsystem2: Fire!\n";}
};/*** The Facade class provides a simple interface to the complex logic of one or* several subsystems. The Facade delegates the client requests to the* appropriate objects within the subsystem. The Facade is also responsible for* managing their lifecycle. All of this shields the client from the undesired* complexity of the subsystem.*/
class Facade {protected:Subsystem1 *subsystem1_;Subsystem2 *subsystem2_;/*** Depending on your application's needs, you can provide the Facade with* existing subsystem objects or force the Facade to create them on its own.*/public:/*** In this case we will delegate the memory ownership to Facade Class*/Facade(Subsystem1 *subsystem1 = nullptr,Subsystem2 *subsystem2 = nullptr) {this->subsystem1_ = subsystem1 ?: new Subsystem1;this->subsystem2_ = subsystem2 ?: new Subsystem2;}~Facade() {delete subsystem1_;delete subsystem2_;}/*** The Facade's methods are convenient shortcuts to the sophisticated* functionality of the subsystems. However, clients get only to a fraction of* a subsystem's capabilities.*/std::string Operation() {std::string result = "Facade initializes subsystems:\n";result += this->subsystem1_->Operation1();result += this->subsystem2_->Operation1();result += "Facade orders subsystems to perform the action:\n";result += this->subsystem1_->OperationN();result += this->subsystem2_->OperationZ();return result;}
};/*** The client code works with complex subsystems through a simple interface* provided by the Facade. When a facade manages the lifecycle of the subsystem,* the client might not even know about the existence of the subsystem. This* approach lets you keep the complexity under control.*/
void ClientCode(Facade *facade) {// ...std::cout << facade->Operation();// ...
}
/*** The client code may have some of the subsystem's objects already created. In* this case, it might be worthwhile to initialize the Facade with these objects* instead of letting the Facade create new instances.*/int main() {Subsystem1 *subsystem1 = new Subsystem1;Subsystem2 *subsystem2 = new Subsystem2;Facade *facade = new Facade(subsystem1, subsystem2);ClientCode(facade);delete facade;return 0;
}

执行结果

Facade initializes subsystems:
Subsystem1: Ready!
Subsystem2: Get ready!
Facade orders subsystems to perform the action:
Subsystem1: Go!
Subsystem2: Fire!

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

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

相关文章

Java容器学习,一文读懂Java容器使用知识文集。

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

SOLIDWORKS Simulation有限元技巧:模拟放置物品时出现的误区

在solidworks simulation中&#xff0c;我们所知simulation能够对零部件和装配体进行有限元分析&#xff0c;而在这种情况下&#xff0c;为了简单分析&#xff0c;同时也为了减少不必要的运算&#xff0c;很多时候我们会选择对整个装配体或是零部件进行简化&#xff0c;如图二&…

免费分享一套Springboot+Vue前后端分离的个人博客系统,挺漂亮的

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringbootVue前后端分离的个人博客系统&#xff0c;分享下哈。 项目视频演示 【免费】SpringbootVue个人博客系统 毕业设计 Java毕业设计_哔哩哔哩_bilibili【免费】SpringbootVue个人博客系统 毕业设计 …

css linear-gradient和radial-gradient的区别

linear-gradient 和 radial-gradient 是 CSS 中用于创建渐变背景的两种主要方法 1.linear-gradient&#xff08;线性渐变&#xff09; linear-gradient 创建沿着一条直线方向的渐变效果。它接受一个角度参数或者两个以上的颜色值作为输入。语法如下&#xff1a; /* 使用角度…

Flink 状态管理与容错机制(CheckPoint SavePoint)的关系

一、什么是状态 无状态计算的例子&#xff1a; 例如一个加法算子&#xff0c;第一次输入235那么以后我多次数据23的时候得到的结果都是5。得出的结论就是&#xff0c;相同的输入都会得到相同的结果&#xff0c;与次数无关。 有状态计算的例子&#xff1a; 访问量的统计&#x…

工业5G路由器提升驾考效率,实现智慧驾考物联网

为了提高驾考的考试效率&#xff0c;更好地服务广大学员&#xff0c;车管所驻考场监控中心结合物联网技术采用智慧驾考系统&#xff0c;实现考场监控、考试员远程监考、学员视频实时回传、自动评判等功能&#xff0c;为驾考公平公正安全提供保障。 该系统由智能监控管理平台和…

深入了解Spring MVC工作流程

目录 1. MVC架构简介 2. Spring MVC的工作流程 2.1. 客户端请求的处理 2.2. 视图解析和渲染 2.3. 响应生成与返回 3. Spring MVC的关键组件 3.1. DispatcherServlet 3.2. HandlerMapping 3.3. Controller 3.4. ViewResolver 4. 结论 Spring MVC&#xff08;Model-Vi…

基于Java版本与鸿鹄企业电子招投标系统的二次开发实践-鸿鹄企业电子招投标系统源代码+支持二开+鸿鹄电子招投标系统

随着市场竞争的加剧和企业规模的扩大&#xff0c;招采管理逐渐成为企业核心竞争力的重要组成部分。为了提高招采工作的效率和质量&#xff0c;我们提出了一种基于电子化平台的解决方案。该方案旨在通过电子化招投标&#xff0c;使得招标采购的质量更高、速度更快&#xff0c;同…

TCP_滑动窗口介绍

简介 TCP协议中有两个窗口&#xff0c;滑动窗口和拥塞窗口&#xff0c;两者均是一种流控机制&#xff1b;滑动窗口是接收方的流控机制&#xff0c;拥塞窗口是发送方的流控机制。 本文介绍滑动窗口&#xff0c;接收方为TCP连接设置了接收缓存。当TCP连接接收到正确、按序的字节…

推荐几个靠谱的视频素材网站,让你的作品更吸引人~

视频素材是视频制作的重要组成部分&#xff0c;它可以提升视频的质量&#xff0c;增加视频的吸引力&#xff0c;让你的视频更容易获得观众的喜爱和关注。如果你想往视频行业发展&#xff0c;或者只是想做一个视频自媒体&#xff0c;那么你一定会遇到一个问题&#xff1a;视频素…

应用在水箱液位检测中的电容传感芯片

水箱水位检测原理通常包括使用传感器来检测水位的变化。常见的传感器类型包括液位开关、液位计和液位传感器。液位开关是一种简单的传感器&#xff0c;它可以检测水位是否达到预定的高度。当水位升高时&#xff0c;开关会打开&#xff1b;当水位降低时&#xff0c;开关会关闭。…

2023年全球架构师峰会(ArchSummit北京站2023)-核心PPT资料下载

一、峰会简介 ArchSummit聚焦业界强大的技术成果&#xff0c;秉承“实践第一、案例为主”的原则&#xff0c;展示先进技术在行业中的典型实践&#xff0c;以及技术在企业转型、发展中的推动作用。旨在帮助技术管理者、CTO、架构师做好技术选型、技术团队组建与管理&#xff0c…

pip 常用指令 pip help 命令用法介绍

&#x1f4d1;pip 常用命令归类整理 pip help 是一个用于获取 Python 包管理器 pip 的帮助信息的命令。它可以帮助我们了解 pip 的各种命令和参数的用法。 pip help 命令的参数主要包括 pip 的各种子命令&#xff0c;例如 install、uninstall、freeze、list 等。你可以使用 p…

MATLAB中var函数用法

目录 语法 说明 示例 矩阵方差 数组方差 指定方差权重向量 指定方差的维度 数组页的方差 排除缺失值的方差 方差和均值 var函数的功能是求取方差。 语法 V var(A) V var(A,w) V var(A,w,"all") V var(A,w,dim) V var(A,w,vecdim) V var(___,nanfla…

2022复盘2023规划(技术篇)

2022复盘&2023规划&#xff08;技术篇&#xff09; 说明&#xff1a; 以下是我对我自身知识掌握度的分级解释 Lv0 简单了解技术点的应用场景 Lv1 前端&#xff1a; 掌握基础知识&#xff0c;能进行简单组件的开发与页面绘制&#xff1b; 后端&#xff1a; 掌握知识点基础&a…

Fabric:使用GoLand+Fabric-SDK-Go操作Fabric网络

遇到bug, 未完待续!!! 写在最前 前序博客已经介绍了使用命令的方式在Fabric上创建通道以及部署执行链码的方法&#xff0c;但这个过程太繁琐&#xff0c;尤其是当Fabric网络中peer节点和组织Org过多时&#xff0c;需要频繁的更改环境变量。 Hyperledger Fabric官方提供了Fabri…

Ubuntu 22.04 LTS上安装Docker-ce

在Ubuntu 22.04 LTS上安装Docker-ce Docker是一个开源平台&#xff0c;用于自动化应用程序的部署、扩展和管理。它使用容器技术&#xff0c;使开发、测试和部署过程更加简化和可靠。本文将介绍在Ubuntu 22.04 LTS上安装Docker-ce的步骤。 步骤1&#xff1a;更新软件包列表 …

5~80V降5V 0.3A同步整流降压型DC-DC转换器-AH7550

AH7550是一种TEL&#xff1a;l86*4884*3702*高压、高效率的同步整流降压型DC-DC开关转换器&#xff0c;恒定120KHz开关频率&#xff0c;输出电流能力高达0.6A&#xff0c;AH7550支持5V~80V的宽输入操作电压范围&#xff0c;同时支持最大占空比90%输出&#xff0c;芯片内置环路补…

小型家用超声波清洗机适合清洗眼镜吗?小型超声波清洗机排行榜

在快节奏的现代生活中&#xff0c;我们的眼睛时常与各种物品接触&#xff0c;如眼镜、隐形眼镜等。为了保持眼睛的健康和舒适&#xff0c;定期清洗眼镜变得尤为重要。而随着科技的发展&#xff0c;小型家用超声波清洗机逐渐进入人们的视野&#xff0c;它能否成为我们清洗眼镜的…

学生备考护眼灯什么牌子好推荐?高性能护眼台灯推荐

作为一名电器测评师&#xff0c;对各类电器好物都了解得比较多&#xff0c;最近也会被很多的人询问护眼台灯哪个牌子好&#xff0c;问其原因才知很多的人有使用护眼台灯出现眼睛酸痛刺眼的现象&#xff0c;这是因为使用了不专业护眼台灯导致的&#xff0c;一般这类护眼台灯使用…