C++设计模式结构型模式———外观模式

文章目录

  • 一、引言
  • 二、外观模式
  • 三、总结

一、引言

外观模式是一种结构型设计模式, 能为程序库、 框架或其他复杂类提供一个简单的接口。也就是说,该模式的目的用于隔离接口,换句话说,就是扮演中间层的角色,把本来结合紧密的两部分模块(系统)隔离开来,让这两部分内容通过中间层来打交道(类似于依赖倒置原则:高层和低层都依赖于抽象层),从而极大地降低了两部分模块之间的耦合性并通过中间层对许多操作进行简化。


二、外观模式

一款游戏可玩的内容越多,或者说功能越多,它的配置项也越多。以《英雄联盟》为例,其配置项就分好多类,例如有图形、声音、语音聊天等,而每个配置项都会有很多可供配置的细节内容,例如:

  • 图形一一是否全屏显示、是否开启特效、窗口分辨率、是否开启抗锯齿等。
  • 声音一一是否开启背景声音、是否开启环境音效、是否开启表情声音、音量大小设
    置等。
  • 聊天设置一一麦克风音量、麦克风灵敏度、聊天音量等。

现在,策划希望在闯关打斗类游戏项目中也引人这些配置项。程序经过思考,准备创建3个类来分别实现图形、声音、语音聊天相关功能,因为这些类在整个项目中只保持一个对象就可以,因此准备将这3个类设置为单例,代码如下:

//图形相关
class graphic
{
public:static graphic& getInstance(){static graphic instance;return instance;}void display(bool enable) {//是否全屏显示(true:是)cout << "图形->是否全屏显示->" << enable << endl;}void effect(bool enable) {//是否开启特效(true:是)cout << "图形->是否开启特效->" << enable << endl;}void resolution(int index) {//设置窗口分辨率cout << "图形->分辨率设置选项->" << index << endl;}void antialiasing(bool enable) {//是否开启抗锯齿(true:是)cout << "图形->是否开启抗锯齿->" << enable << endl;}
private:graphic() {}graphic(const graphic&) = delete;graphic& operator=(const graphic&) = delete;~graphic() {}
};
//声音相关class sound
{
public:static sound& getInstance(){static sound instance;return instance;}void bgsound(bool enable) {cout << "声音->是否开启背景声音->" << enable << endl;}void envirsound(bool enable) {cout << "声音->是否开启环境声音->" << enable << endl;}void expsound(bool enable) {cout << "声音->是否表情环境声音-" << enable << endl;}void setvolume(int level) {cout << "声音->音量大小为->" << level << endl;}
private:sound() {}sound(const sound&) = delete;sound& operator=(const sound&) = delete;~sound() {}
};// 语音聊天相关类
class chatvoice {
private:chatvoice() {};chatvoice(const chatvoice&) = delete;chatvoice& operator=(const chatvoice&) = delete;~chatvoice() {};
public:static chatvoice& getInstance() {static chatvoice instance;return instance;}
public:void micvolume(int level)//麦克风音量大小设置(0~100){cout << "语音聊天->麦克风音量大小为->" << level << endl;}void micsens(int level)//麦克灵敏度设置(0~100){cout << "语音聊天->麦克风灵敏度为->" << level << endl;}void chatvolume(int level)//聊天音量设置(0~100){cout << "语音聊天->聊天音量为->" << level << endl;}
};

从上面的代码中可以看到一个问题,一个项目中可能有多个单例类,从而造成了一些代码冗余,例如这些单例类的构造函数、拷贝构造函数、拷贝赋值运算符以及析构函数只有名字不同,每个单例类也都需要有getInstance成员函数。所以实际上可以实现一个单例类模,通过单例类模板可以避免多个单例类的定义中造成的代码冗余。

我们使用如下代码来实现对游戏中的图形、声音、语音的处理::

graphic& g_gp = graphic::getInstance();
g_gp.display(false);
g_gp.effect(true);
g_gp.resolution(2);
g_gp.antialiasing(false);
cout << "**********" << endl;
sound& g_snd = sound::getInstance();
g_snd.setvolume(80);
g_snd.envirsound(true);
g_snd.bgsound(false);
cout << "**********" << endl;
chatvoice & g_cv = chatvoice::getInstance();
g_cv.chatvolume(70);
g_cv.micsens(65);/*
图形->是否全屏显示->0
图形->是否开启特效->1
图形->分辨率设置选项->2
图形->是否开启抗锯齿->0
**********
声音->音量大小为->80
声音->是否开启环境声音->1
声音->是否开启背景声音->0
**********
语音聊天->聊天音量为->70
语音聊天->麦克风灵敏度为->65
*/

我们可以把上述三各类称为业务类,实际使用这些类接口的代码就是客户端代码。上述代码比较简单,实际上是业务类彼此之间还可能有相互调用关系。而且客户端代码可能出现在任何地方,因此,客户端代码和业务类直接交互是比较麻烦的。

如果业务类是第三方机构编写的,我们可以建议第三方机构设计一个新的类,隔在客户端代码和业务类之间,扮演中间层的角色。客户端不再与业务类交互,而是直接与新的类打交道。这个新的类扮演的就是外观模式的角色。

对客户端提供一些简单的调用接口(这些接口往往可以实现一系列动作),通过接口与业务类打交道来大大简化客户端代码直接与业务类打交道时的复杂性。
在这里插入图片描述

下面我们设计一个conffacade类,提供两个接口,一个用于较高配置的电脑,一个用于较低配置的。

//扮演外观模式角色的类
class conffacade {
public:conffacade(const conffacade&) = delete;conffacade& operator=(const conffacade&) = delete;~conffacade() {}static conffacade& getInstance() {static conffacade instance;return instance;}void LowConfComputer()//对于低配置计算机,只开启一些低配置选项{graphic& g_gp = graphic::getInstance();g_gp.display(true);		//全屏耗费资源更低g_gp.effect(false);g_gp.resolution(2);g_gp.antialiasing(false);sound& g_snd = sound::getInstance();g_snd.bgsound(false);g_snd.envirsound(false);g_snd.expsound(false);g_snd.setvolume(15);chatvoice& g_cv = chatvoice::getInstance();g_cv.micvolume(20);g_cv.micsens(50);g_cv.chatvolume(60);}void HighConfComputer() {//对于高配置计算机,能达到最好效果的项全部开graphic& g_gp = graphic::getInstance();g_gp.display(false);g_gp.effect(true);g_gp.resolution(0);g_gp.antialiasing(true);sound& g_snd = sound::getInstance();g_snd.bgsound(true);g_snd.envirsound(true);g_snd.expsound(true);g_snd.setvolume(50);chatvoice& g_cv = chatvoice::getInstance();g_cv.micvolume(100);g_cv.micsens(100);g_cv.chatvolume(100);}
private:conffacade() {}
};

这样的话我们就可以使用如下代码来达到目的:

conffacade& g_cffde = conffacade::getInstance();
cout << "低配置计算机,调用LowConfComputer接口" << endl;g_cffde.LowConfComputer();
cout << "********" << endl;
cout << "高配置计算机,调用HighConfComputer接口" << endl;
g_cffde.HighConfComputer();

如果客户端代码和业务类之间直接进行交互,那么两者的耦合度就会比较高,当对业务类代码进行修改时,可能也需要同时修改客户端的代码。通过引入扮演外观模式角色的类conffacade,将客户端与具体的复杂业务类接口分隔开,客户端只需要与外观角色打交道,而不需要与业务类内部的很多接口打交道,这就降低了客户端和业务类之间的代码耦合度。

外观模式结构

在这里插入图片描述

一般来说,外观模式包含两种角色:

  • 外观角色Facade):conffacade类扮演着这个角色,客户端可以调用这个角色的方法(成员函数),该方法将客户端的请求传递到业务类中去处理。
  • 子系统角色SubSystem):子系统就是前面提到的业务类,项目中可以有多个子系统,每个子系统也可以是一个单独的类,也可以是彼此之间具有耦合关系的类,这些角色可以被外观角色调用,也可以被客户端直接调用。

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

引入外观模式的定义(设计意图):提供了一个统一的接口来访问子系统中的一群接口。外观模式定义了一个高层接口,让子系统更容易调用。


三、总结

外观模式定义一组高层接口或者说提供子系统的一个到多个简化接口让子系统更容易使用以及让操作变得更直接,让客户端和子系统实现解耦。当子系统发生改变时,一般只需要修改外观类而基本不需要修改客户端代码(虽然这种修改有点违背开闭原则)。

外观模式提供的接口给客户端调用带来了很大的方便,使客户端无须关心子系统的工作细节,但这些接口也许缺乏灵活性,当然,如果客户端认为有必要对子系统进行最灵活的控制,那么依然可以绕过外观模式类而直接使用子系统。

一个项目中可以有多个外观类,每个外观类都负责与一些特定的子系统进行交互,即便是同一个子系统,也可以有多个外观类。外观模式为客户端和子系统之间提供了一种简化的交互渠道,但并没有为子系统增加新的行为,如果希望增加新的行为,则应该通过修改子系统角色来实现。

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

再举一个更易理解的例子,普通照相机与傻瓜相机,傻瓜照相机就扮演着外观模式的角色,傻瓜照相机不需要拍照者调整光圈等直接按下快门即可拍照。

外观模式为现有对象定义了一个新接口; 适配器模式则会试图运用已有的接口。 适配器通常只封装一个对象;外观通常会作用于整个对象子系统上。

当只需对客户端代码隐藏子系统创建对象的方式时,可以使用抽象工厂模式来代替外观。

享元模式展示了如何生成大量的小型对象, 外观则展示了如何用一个对象来代表整个子系统。

外观和中介者模式的职责类似: 它们都尝试在大量紧密耦合的类中组织起合作。外观为子系统中的所有对象定义了一个简单接口, 但是它不提供任何新功能。 子系统本身不会意识到外观的存在。 子系统中的对象可以直接进行交流。中介者将系统中组件的沟通行为中心化。 各组件只知道中介者对象, 无法直接相互交流。

外观类通常可以转换为单例模式类, 因为在大部分情况下一个外观对象就足够了。外观与代理模式的相似之处在于它们都缓存了一个复杂实体并自行对其进行初始化。 代理与其服务对象遵循同一接口, 使得自己和服务对象可以互换, 在这一点上它与外观不同。

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

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

相关文章

软件设计师:排序算法总结

一、直接插入 排序方式&#xff1a;从第一个数开始&#xff0c;拿两个数比较&#xff0c;把后面一位跟前面的数比较&#xff0c;把较小的数放在前面一位 二、希尔 排序方式&#xff1a;按“增量序列&#xff08;步长&#xff09;”分组比较&#xff0c;组内元素比较交换 假设…

Tips:如何选择最佳邮件群发工具?

在数字营销的世界中&#xff0c;电子邮件仍然是与客户沟通的最有效方式之一。无论是推广新产品、发送新闻简报&#xff0c;还是进行客户关系管理&#xff0c;邮件群发工具都扮演着至关重要的角色。然而&#xff0c;市场上有如此多的选择&#xff0c;如何才能找到最适合您业务需…

智算中心建设热潮涌动 AI服务器赋能加速

在数字化浪潮汹涌的今天&#xff0c;人工智能新技术新发展日新月异。智算作为数字经济时代的新生产力&#xff0c;正逐步成为推动经济社会高质量发展的新引擎。 智算中心&#xff1a;算力时代的“新基建” 近年来&#xff0c;随着人工智能技术的迅猛发展和广泛应用&#xff0c;…

vue输入中文,获取英文首字母缩写

背景&#xff1a;要求输入中文的时候&#xff0c;系统给出对应的首字母大写&#xff0c;作为拼音。 例如&#xff1a;输入“博客”&#xff0c;输出‘BK’ 等等…… 经查&#xff1a;使用 js-pinyin 这个第三方插件即可实现 1. 下载依赖 npm install js-pinyin 或者 yarn ad…

如何构建一个呼叫中心客服部门

如何构建一个呼叫中心客服部门 作者&#xff1a;开源呼叫中心系统 FreeIPCC&#xff0c;Github地址&#xff1a;https://github.com/lihaiya/freeipcc 一、引言 呼叫中心客服部门是现代企业与客户之间沟通的重要桥梁&#xff0c;其构建不仅关乎企业的客户服务质量&#xff0…

【Python进阶】Python中的网络爬虫策略:高效数据抓取与解析

1、网络爬虫概论与Python环境配置 1.1 网络爬虫基本概念与工作原理 网络爬虫&#xff0c;如同在网络世界中勤劳的蚂蚁&#xff0c;自动地在网络空间里穿梭游走&#xff0c;寻找并收集散布在网络各个角落的信息宝藏。它是一种自动化程序&#xff0c;遵循一定的规则&#xff0c…

【机器学习】随机森林算法

随机森林&#xff08;Random Forest&#xff09;是一种集成学习算法&#xff0c;它结合了多个决策树的输出&#xff0c;以提高预测的准确性和稳定性。随机森林被广泛应用于分类和回归任务中&#xff0c;尤其适用于数据特征之间存在非线性关系或噪声的情况。 在本文中&#xff…

十四届蓝桥杯STEMA考试Python真题试卷第二套第三题

来源:十四届蓝桥杯STEMA考试Python真题试卷第二套编程第三题 通过这道字符串处理的解析,重点学习字典的 get() 方法和 map() 函数的应用。我们同时也给出了“一行代码”的实现方案,令人惊叹的是到两种实现方法的代码行数差不多是10:1。这次,我们更推荐一行代码的实现方式,…

数据结构与算法--回溯法

回溯法 1 括号生成分析&#xff1a; 2 解数独分析代码 回溯法本质是的暴力枚举/遍历法&#xff0c;一般用递归实现。 当我们可以把问题分解为若干个步骤&#xff0c;每个步骤都有若干个选择的时候&#xff0c;若需要列出所有解答形式&#xff0c;则采用枚举法。 1 括号生成 数…

数据集收集器0.3

为了进一步完善代码,使其能够识别更多格式的问答对,并且在遇到无法识别的格式时能够跳过并继续处理下一个,我们可以采取以下措施: 增强正则表达式:支持更多的问答对格式。 增加容错处理:在遇到无法识别的格式时,记录错误并继续处理下一个。 多模式匹配:支持多种问答对…

外卖小程序的研究与开发ssm+论文源码调试讲解

2系统关键技术 2.1微信小程序 微信小程序&#xff0c;简称小程序&#xff0c;英文名Mini Program&#xff0c;是一种全新的连接用户与服务的方式&#xff0c;可以快速访问、快速传播&#xff0c;并具有良好的使用体验。 小程序的主要开发语言是JavaScript&#xff0c;它与普通…

花了6000多考下PMP却不会用?你真的懂PMP实际用法吗?

大家都已经下载了PMP的电子版证书吗&#xff1f;虽然拿到了电子证书&#xff0c;但很多人又开始期待纸质版证书。不要着急&#xff0c;考试后需要6-9个月才能拿到纸质版证书&#xff0c;可能还需要等一段时间。 电子证书和纸质证书具有同样的有效性&#xff0c;需要使用证书时…

Spring面向切面编程

目录 1.AOP概述及Spring AOP实现原理 AOP概述 AOP的应用场景 AOP的作用 Spring AOP概述 Spring AOP的实现原理 Spring AOP中Advice的分类 2. 通过xml配置实现AOP 实现步骤&#xff1a; 新增模块&#xff1a; 导入相关依赖&#xff1a; 新增实体类User 新增业务类UserS…

MySQL 和 PostgreSQL 的对比概述

MySQL 和 PostgreSQL 是两种广泛使用的开源关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它们各自有其特点和优缺点。以下将从多个方面对它们进行详细比较。 1. 介绍 MySQL&#xff1a; MySQL 由瑞典公司 MySQL AB 开发&#xff0c;2008 年被 Sun Microsyst…

[C++] GDB的调试和自动化检测

文章目录 GDB基本使用1. bazel的debug过程2. line-tables-only的使用 Reference GDB基本使用 参考文档&#xff1a; https://zhuanlan.zhihu.com/p/655719314 1. bazel的debug过程 需要带--copt-g --copt-ggdb选项进行编译 // bazel build --stripnever --copt-g --copt-ggd…

urllib3.exceptions.Proxyschemeunknown: Proxy URL had no scheme

这个错误信息 urllib3.exceptions.Proxyschemeunknown: Proxy URL had no scheme, should start with http:// or https:// 表示你在使用 urllib3 库设置代理时&#xff0c;提供的代理 URL 没有包含正确的协议方案&#xff08;scheme&#xff09;。在 URL 中&#xff0c;方案部…

Javaweb选课系统-开源计划-起源-001-完全免费开源

项目部署&#xff0c;效果视频 https://www.bilibili.com/video/BV1LMDUY8Ef7/?spm_id_from333.880.my_history.page.click&vd_source17d16b2e328f19328e077e9cb07565ef项目地址&#xff1a; https://gitee.com/lucky-six/Javaweb-xuanke

微服务架构面试内容整理-微服务与传统单体架构的区别

微服务和传统单体架构的主要区别在于架构设计和服务管理方式: 1. 架构设计: ● 单体架构:整个应用作为一个整体构建,所有功能模块紧密耦合在一起,通常在一个代码库中管理。 ● 微服务架构:应用被拆分为多个独立的服务,每个服务专注于特定的业务功能,可以独立开发和部署…

【简信CRM-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

SAP 学习文章

SAP PP模块后台表关系图_sap常用表及关系图-CSDN博客 SAP_PP模块流程_sap中pp模组指令-CSDN博客