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;组内元素比较交换 假设…

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

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

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

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

外卖小程序的研究与开发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…

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

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

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

Linux云计算 |【第五阶段】PROJECT3-DAY1

主要内容&#xff1a; 跳板机&#xff08;堡垒机&#xff09;的概念、部署JumpeServer 一、跳板机&#xff08;堡垒机&#xff09;的概念 跳板机&#xff08;Jump Server 或 Bastion Host&#xff09;是一种网络安全设备或服务器&#xff0c;也称堡垒机&#xff0c;是一类可作…

宠物空气净化器哪个牌子好?希喂、352两款产品吸力、噪音真实测试

我身为养宠博主&#xff0c;这些年用过不少宠物空气净化器&#xff0c;花费了1w&#xff0c;对很多产品都进行过测评。正值双十一&#xff0c;很多朋友都在问我宠物空气净化器到底有没有必要买&#xff1f;答案毫无疑问是有必要&#xff01; 相比较于其他清理工具&#xff0c;…

Clang-Tidy 是什么?如何让你的代码更干净无瑕

Clang-Tidy&#xff1a;让你的代码更干净&#xff0c;让潜在问题无处遁形 在现代软件开发中&#xff0c;代码质量不再仅仅体现在功能实现上&#xff0c;还包括其可维护性、可读性和潜在问题的检测。clang-tidy 是一款功能强大的静态分析工具&#xff0c;专为 C/C 代码而生&…

微服务中常用分布式锁原理及执行流程

1.什么是分布式锁 分布式锁是一种在分布式系统环境下实现的锁机制&#xff0c;它主要用于解决&#xff0c;多个分布式节点之间对共享资源的互斥访问问题&#xff0c;确保在分布式系统中&#xff0c;即使存在有多个不同节点上的进程或线程&#xff0c;同一时刻也只有一个节点可…

【算法】(Python)动态规划

动态规划&#xff1a; dynamic programming。"programming"指的是一种表格法&#xff0c;而非编写计算机程序。通常解决最优化问题&#xff08;optimization problem&#xff09;。将问题拆分成若干个子问题&#xff0c;求解各子问题来得到原问题的解。适用于多阶段…

PySpark本地开发环境搭建

一.前置事项 请注意&#xff0c;需要先实现Windows的本地JDK和Hadoop的安装。 二.windows安装Anaconda 资源&#xff1a;Miniconda3-py38-4.11.0-Windows-x86-64&#xff0c;在window使用的Anaconda资源-CSDN文库 右键以管理员身份运行&#xff0c;选择你的安装路径&#x…

深度学习经典模型之ZFNet

1 ZFNet 1.1 模型介绍 ​ ZFNet是由 M a t t h e w Matthew Matthew D . Z e i l e r D. Zeiler D.Zeiler和 R o b Rob Rob F e r g u s Fergus Fergus在AlexNet基础上提出的大型卷积网络&#xff0c;在2013年ILSVRC图像分类竞赛中以11.19%的错误率获得冠军&#xff08;实际…

2024网盘搜索引擎合集推荐:高效搜索资源的利器

2024网盘搜索引擎合集推荐&#xff1a;高效搜索资源的利器 在这个信息爆炸的时代&#xff0c;找到合适的资源变得越来越重要。以下是一些网盘搜索引擎的推荐&#xff0c;它们可以帮助您快速找到所需的文件和资料。 咔帕搜索&#xff1a;简单高效的云盘搜索 网址&#xff1a;…

最新榜单!国内免费好用的OA协同软件前十名

在现代企业管理中&#xff0c;OA&#xff08;Office Automation&#xff09;协同软件已成为提升工作效率、简化沟通流程的重要工具。OA协同软件的主要功能涵盖任务管理、文件共享、审批流程、日程安排等&#xff0c;从而帮助企业更高效地进行跨部门协作、信息传递和项目跟踪。在…

Java自动点名器实现案例详解

Java自动点名器实现案例详解 在教学管理中&#xff0c;点名是一项重要的任务。随着技术的发展&#xff0c;使用编程语言实现自动化的点名器不仅可以提高效率&#xff0c;还能增加课堂的互动性和趣味性。本文将详细介绍三个案例&#xff0c;分别是简单随机点名器、带有权重的随…

揭秘规则引擎:如何实现多版本无感切换与数据源同步

在现代业务系统中&#xff0c;规则决策引擎能够自动化处理复杂的业务逻辑。为了满足不断变化的业务需求&#xff0c;同时确保系统运行的连续性和稳定性&#xff0c;在JVS规则引擎中提供了多版本无感发布和数据源变更日志同步功能。 多版本无感发布 多版本无感发布主要适用于已…

【Python3】【力扣题】409. 最长回文串

【力扣题】题目描述&#xff1a; &#xff08;题意理解&#xff09;统计如下&#xff1a; ① 字母个数本身是偶数。 ② 字母个数是奇数&#xff0c;统计奇数中的偶数部分&#xff0c;例如&#xff1a;字母个数为3&#xff0c;统计其中的2。 ③ 中间可以有一个奇数字母。即只要有…