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

news2024/11/23 2:44:37

文章目录

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

一、引言

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


二、外观模式

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

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

现在,策划希望在闯关打斗类游戏项目中也引人这些配置项。程序经过思考,准备创建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.coloradmin.cn/o/2231747.html

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

相关文章

物流公司出货单据模板下载软件 佳易王物流单管理系统操作使用教程

一、概述 【软件资源文件下载可以点文章最后信息卡片】 物流公司出货单据模板下载软件物流单管理系统操作使用教程 ‌软件功能‌&#xff1a; ‌记录管理‌&#xff1a;记录运费、垫付货款、代收货款、保险费等。‌打印模式‌&#xff1a;支持空白单、卷纸、印刷好的单子等多…

谷粒商城のsentinelzipkin

文章目录 前言一、Sentinel1、什么是Sentinel2、项目配置3、使用案例3.1、流控3.2、降级3.3、黑白名单设置 二、Zipkin1、什么是Zipkin2、项目配置3、整合案例 前言 本篇介绍Spring Cloud Ali的sentinel组件&#xff0c;用于对微服务的熔断降级&#xff0c;以及链路追踪zipkin的…

WPF+MVVM案例实战(十九)- 自定义字体图标按钮的封装与实现(EF类)

文章目录 1、案例效果1、按钮分类2、E类按钮功能实现与封装1.文件创建与代码实现2、样式引用与封装 3、F类按钮功能实现与封装1、文件创建与代码实现2、样式引用与封装 3、按钮案例演示1、页面实现与文件创建2、运行效果如下 4、源代码获取 1、案例效果 1、按钮分类 在WPF开发…

keepalived + nginx 实现网站高可用性(HA)

keepalive 一、keepalive简介二、实现步骤1. 环境准备2. 安装 Keepalived3. 配置 Keepalived 双机主备集群架构4. 配置 Nginx5. 启动Keepalived6. 测试高可用性7. 配置keepalived 双主热备集群架构 一、keepalive简介 目前互联网主流的实现WEB网站及数据库服务高可用软件包括&a…

破局智能制造:难点分析与对策

一、 智能制造过程中可能遇到难点: 1. --概念和技术繁多--: - 智能制造领域涉及众多概念和技术,如工业4.0、CPS、工业互联网等,让企业难以选择和应用。 2. --缺乏经验和成功案例--: - 企业在推进智能制造时缺乏经验,存在信息孤岛、自动化孤岛等问题,缺乏统一规划和系统推…

中汽测评观察 亲子出行健康为先,汽车健康用材成重要考量

在中国&#xff0c;亲子出行是驾车的主要场景之一。汽车不仅仅是一种便捷的交通工具&#xff0c;更是生活中的移动“第三空间”。在此背景下&#xff0c;汽车健康用材不仅是消费者关注的焦点问题&#xff0c;也成为汽车企业发力的重要方向。 对消费者而言&#xff0c;在家庭亲子…

阿里巴巴Seata分布式事务解决方案

Seata是什么 Seata是一款开源的分布式事务解决方案&#xff0c;致力于在微服务架构下提供高性能和简单易用的分布式事务服务。 当开发框架为spring boot或者SSM&#xff0c;都可以使用Seata进行开发。 分布式事务是什么 在大型架构中&#xff0c;一般会把一个应用系统&#x…

Cuebric:用AI重新定义3D创作的未来

一、简介 Cuebric 是一家成立于2022年夏天的好莱坞创新公司,致力于为电影、电视、游戏和时尚等行业提供先进的AI多模态SaaS平台。自2024年1月正式推出以来,Cuebric 已经在市场上获得了广泛的认可和积极的反馈。目前,该平台正处于1.0版本的beta测试阶段,已募集约50万美元的…

【Spring IoCDI】IoC容器,IoC注解,Bean的使用

【Spring核心思想:IoC】 spring是一个开源框架&#xff0c;支持广泛的应用场景&#xff0c;简而言之:Spring是包含了众多工具方法的IoC容器 【IoC】 IoC的意思是「控制反转」&#xff0c;也就是说Spring是一个“控制反转”的容器 通用程序的实现代码&#xff0c;类的创建顺序…

Android笔记(三十一):Deeplink失效问题

背景 通过deeplink启动应用之后&#xff0c;没关闭应用的情况下&#xff0c;再次使用deeplink会失效的问题&#xff0c;是系统bug导致的。此bug仅在某些设备&#xff08;Nexus 5X&#xff09;上重现&#xff0c;launchMode并且仅当应用程序最初通过深层链接启动并再次通过深层…

深入理解Transformer中的位置编码

1 位置编码的作用 由于注意力的作用机制&#xff0c;不论输入序列的顺序如何&#xff0c;输出结果都是一样的。 也就是丢失了位置信息。 但是对于语言模型&#xff0c; 我们都知道顺序是很重要的&#xff0c; 所以需要对输入序列额外注入位置信息。 2 位置编码方式 Transfor…

Ansible 部署应用

Ansible Ansible 是基于 Python 开发&#xff0c;集合了众多优秀运维工具的优点&#xff0c;实现了批量运行命令、部署程序、配置系统等功能的自动化运维管理工具。默认通过 SSH 协议进行远程命令执行或下发配置&#xff0c;无需部署任何客户端代理软件&#xff0c;从而使得自动…

根据问题现象、用户操作场景及日志打印去排查C++软件问题,必要时尝试去复现问题

目录 1、概述 2、通过现有信息无法定位问题时&#xff0c;则需要尝试去复现问题 3、非崩溃问题与崩溃问题的一般排查思路 3.1、非崩溃问题的排查思路 3.2、崩溃问题的排查思路 4、难以复现问题的可能原因总结 4.1、问题难以复现&#xff0c;可能和某种特殊的业务场景或操…

STL——string(2)

博客ID&#xff1a;LanFuRenC系列专栏&#xff1a;C语言重点部分 C语言注意点 C基础 Linux 数据结构 C注意点 今日好题 声明等级&#xff1a;黑色->蓝色->红色 欢迎新粉加入&#xff0c;会一直努力提供更优质的编程博客&#xff0c;希望大家三连支持一下啦 目录 1) …

Spark的集群环境部署

一、Standalone集群 1.1、架构 架构&#xff1a;普通分布式主从架构 主&#xff1a;Master&#xff1a;管理节点&#xff1a;管理从节点、接客、资源管理和任务 调度&#xff0c;等同于YARN中的ResourceManager 从&#xff1a;Worker&#xff1a;计算节点&#xff1a;负责利…

【大数据学习 | kafka】kafka的数据存储结构

以上是kafka的数据的存储方式。 这些数据可以在服务器集群上对应的文件夹中查看到。 [hexuanhadoop106 __consumer_offsets-0]$ ll 总用量 8 -rw-rw-r--. 1 hexuan hexuan 10485760 10月 28 22:21 00000000000000000000.index -rw-rw-r--. 1 hexuan hexuan 0 10月 28 …

【Leecode】Leecode刷题之路第40天之组合总和II

题目出处 40-组合总和II-题目出处 题目描述 个人解法 思路&#xff1a; todo代码示例&#xff1a;&#xff08;Java&#xff09; todo复杂度分析 todo官方解法 40-组合总和II-官方解法 方法1&#xff1a;回溯 思路&#xff1a; 代码示例&#xff1a;&#xff08;Java&…

网络编程入门——网络原理初识

一、网络发展史 1.1 独立模式 即计算机之间相互独立&#xff0c;互不连通的。 1.2 网络互联 即将多台计算机连接在一起&#xff0c;完成数据共享。 数据共享本质是⽹络数据传输&#xff0c;即计算机之间通过⽹络来传输数据&#xff0c;也称为⽹络通信。 根据网络互联规模的不…

关于爬虫需要了解的基础知识 (一、 http协议)

声明 文章仅供学习与交流&#xff01;严禁用于任何商业与非法用途&#xff01;否则由此产生的一切后果均与作者无关&#xff01; 一、何为爬虫 爬虫&#xff08;Crawler&#xff09;是一种按照既定规则&#xff0c;在网络上自动爬取信息的程序或脚本&#xff0c;也称为网际网…

VidPanos:从随手拍摄的平移视频生成全景视频

在当今数字化时代,视频拍摄已经成为人们记录生活和分享经历的重要方式。然而,普通手机拍摄的视频往往受到视角的限制,无法完整地展现一个广阔的场景。今天,我们要介绍的 VidPanos 技术,为解决这个问题提供了一种创新的方法。 VidPanos 是由来自华盛顿大学、谷歌 DeepMind…