设计模式系列-外观模式

news2024/12/24 9:42:38

一、上篇回顾

上篇我们主要讲述了创建型模式中的最后一个模式-原型模式,我们主要讲述了原型模式的几类实现方案,和原型模式的应用的场景和特点,原型模式

适合在哪些场景下使用呢?我们先来回顾一下我们上篇讲述的3个常用的场景。

1、我们在运行态的时候,动态的创建一个动态类型的对象的时候,可能我们使用原型模式,可以动态的创建指定类型的副本,这无疑是好的选择,否

则如果通过我们前面讲述的几个创建型模式来实现的话,效率和代价上是非常大的。

2、有的时候我们需要对比一个对象在处理前和处理后进行对象状态的对比,对比是否处理后对象的状态是否发生变化,或者是其他的要求。这个时候

通过原型模式来克隆对象的副本,远比通过引入其他的Factory或者abstract Factory 来的有效和更容易实现。

3、如果我们发现有一类这样的对象,这类对象通常来说比较简单,并且这类对象之间的差别很有规律,并且这类对象数量一般有限,那么这个时候,

我们通过原型模式来做的话,通过一个对象来复制创建其他类型的对象可能比通过引入其他的Factory或者abstract Factory 更容易实现,而且只需要对

象本身提供一个Clone()方法即可。

4、有的时候我们的项目中有这样的情况,我们是在别人的功能的基础上进行扩展,我们有不能修改现有的程序,如果这个应用程序是基于其他类型的

创建型模式,那么如果我们在系统中新增一个类型的时候,我们需要修改统一的创建型模式中的代码,不管是修改配置文件还是具体的功能代码,无疑都是

要修改的,那么如果我们通过原型模式的话,只需要在新增类型的对象内部,提供一个克隆方法即可,完成新对象的创建。

通过上面的情况,那么我们也能大概看出来原型模式的有一个前提,就是必须是基于对象之上调用Clone()方法完成对象的复制,如果没有创建这个对

象的话,可能不能直接使用该方法。

我们也讲述了,对于Clone()对象的时候,深复制和浅复制的情况,还包括通过序列化对象的形式来完成对象的深复制。

二、摘要

本文主要是讲述结构型模式中一个比较常用的模式-外观模式,这个模式呢, 有个最大的特点将细粒度的对象包装成粗粒度的对象 ,应用程序通过

访问这个外观对象,来完成细粒度对象的调用,外观模式一般是分布式应用和系统架构中的应用服务层的设计中常用的方式,并且一般结合外观模式+DTO

来完成服务层的设计,提供分布式应用服务的高效服务,外观模式我们可以这样理解,我们通过外观的包装,使应用程序只能看到外观对象,而不会看到具

体的细节对象,这样无疑会降低应用程序的复杂度,并且提高了程序的可维护性。本文将会从以下几个方面进行讲述:

1、外观模式的使用场景和特点

2、外观模式的实现方案。

3、总结外观模式。

我们这里先给出一个外观模式的原理图:

[外链图片转存中…(img-uEw5sy36-1694043709816)]这是未使用外观模式之前的情况,下面给出使用外观模式后的情形:

clip_image002通过外观对象来组织细粒度的服务的调用,外观对象提供给外部应用程序可

以使用的服务,而具体的调用细粒度的过程则被外观对象给封装起来,当然这个过程就是封装变化的部分,而将变化的部分与应用程序进行隔离,无疑对程

序的易用性和可维护性都是很大的提高。

三、本文大纲

a、上篇回顾。

b、摘要。

c、本文大纲。

d、外观模式的特点及使用场景。

e、外观模式的实现方案。

f、外观模式使用总结。

g、系列进度。

h、下篇预告。

四、外观模式的特点及使用场景

外观模式的主要思想是将复杂的细粒度的对象服务包装成简单的易使用的粗粒度的功能服务,我们大家最容易理解和知道的外观模式就是,使用的API

接口的封装,我们将第三方的API接口引入到我们的项目中的时候,我们需要对这些接口进行包装,将细粒度的具体调用过程进行包装成外观类的形式,通

过外观类来进行统一的调用。我们平时把一些常用的公共方法也可以简易的称之为外观模式,我们将复杂的细粒度的功能,包装成一个比较通用的简易的的

粗粒度的功能。我们来看看哪些场景下,我们使用外观模式很适合呢?

1、我们在使用第三方类库或者API的时候,我们通过本地的API接口的封装,来完成对第三方API接口的粗粒度外观对象,通过这个外观对象可以很容

易的完成服务的调用。我们这里举例说明吧,例如现在我有一个发送手机短信的API接口,是第三方提供给我的API接口,那么我如何包装呢?

下面给出对API封装的相关代码和说明

public class MessageHelper
{
private static readonly MessageHelper instance = new MessageHelper();

#region API接口

[DllImport(“EUCPComm.dll”, EntryPoint = “SendSMS”)] //即时发送
private static extern int SendSMS(string sn, string mn, string ct, string priority);

[DllImport(“EUCPComm.dll”, EntryPoint = “SendSMSEx”)] //即时发送(扩展)
private static extern int SendSMSEx(string sn, string mn, string ct, string addi, string priority);

[DllImport(“EUCPComm.dll”, EntryPoint = “SendScheSMS”)] // 定时发送
private static extern int SendScheSMS(string sn, string mn, string ct, string ti, string priority);

#endregion

#region 对上面的API包装后的方法

public int SendSMSEx1(string sn, string mn, string ct, string addi, string priority)
{
return SendSMSEx(sn, mn, ct, addi, priority);
}

public int SendSMS1(string sn, string mn, string ct, string priority)
{
return SendSMS(sn, mn, ct, priority);
}

public int SendScheSMS1(string sn, string mn, string ct, string ti, string priority)
{
return SendScheSMS(sn, mn, ct, ti, priority);
}

#endregion
}

相关的测试代码:

static void Main(string[] args)
{
//相关的测试代码
//调用外观对象中的服务
MessageHelper.instance.SendSMS1(“”, “”, “”, “”);
}

2、我们在架构设计的过程中,一次的功能访问可能需要同时的调用很多个对象,那么如果我们在服务调用的时候,能够在应用程序调用中一次就能完

成所有要同时调用的对象那该多好啊,外观模式无疑是最好的原则,特别是在分布式应用中,通过远程调用服务,通过外观模式降低应用程序与服务的交互

次数,同时可以降低应用程序的复杂性。外观模式+DTO,提供远程服务调用的性能,这些都是好的设计方式。我们来看看简单的示例吧,我想我们更

能了解其中的奥妙。看图说话吧,我们这里以一次远程同步服务为例。

未使用外观模式前:

clip_image003一个简单的同步服务,由于应用程序在这次服务中为了完成同步操作,必须进行3次远程连接来进

行把3个对象进行同步,那么如果我们使用外观对象之后呢,我们只需要访问一次即可完成3个对象的同步。这无疑提高了系统的性能和效率。

[外链图片转存中…(img-uNaCtzwz-1694043709817)]并且通过DTO来进行传输的话,可以提供远程服务调用的访问此时和效率。

五、外观模式的实现方案

5.1 外观模式的经典实现

我们先来看看外观模式的经典实现,我们这里已二进制流序列化服务外观为例来说明下经典实现吧

定义一个二进制序列化外观类:

public class SerializationFacede
{
public string BinarySerializationObjToString(object target)
{
using (MemoryStream stream = new MemoryStream())
{
new BinaryFormatter().Serialize(stream, target);

byte[] targetArray = stream.ToArray();

return Convert.ToBase64String(targetArray);
}
}

public object DerializationStringToObj(string target)
{
byte[] targetArray = Convert.FromBase64String(target);
using (MemoryStream stream = new MemoryStream(targetArray))
{
return new BinaryFormatter().Deserialize(stream);
}
}

public T Derialization(string target)
{
return (T)DerializationStringToObj(target);
}
}

我们这里给出相关的测试代码:

static void Main(string[] args)
{
//外观类
SerializationFacede facede = new SerializationFacede();
//测试对象类
Product product = new Product();

//序列化对象
string productString= facede.BinarySerializationObjToString(product);
//反序列化对象
product = facede.Derialization(productString);
}

通过上面的代码我们可以看出,其实外观类也可以以静态方法的形式来提供,提供一个统一的访问入口,这里可以使用我们前面讲述的单例模式来解决这个

问题。或者提供一个外观对象的创建型工厂来完成创建,而不是通过new()的方式来使用。

我们可以改进一下上面的外观模式,通过定义接口,来解耦客户端程序的调用,通过提供一个接口,客户调用通过接口的形式来调用,无疑就可以解

决这样依赖关系:

我们先来看看接口的定义形式:

///
/// 序列化服务的外观接口
///
public interface ISerializationFace
{
string SerializableToString(object target);

object DerializableToObject(string target);

T Derializable(string target);
}

我们分别实现SOAP与二进制的形式来实现我们定义的服务外观接口:

二进制序列化服务:

public class BinarySerializationFace : ISerializationFace
{
#region ISerializationFace 成员

public string SerializableToString(object target)
{
throw new NotImplementedException();
}

public object DerializableToObject(string target)
{
throw new NotImplementedException();
}

public T Derializable(string target)
{
throw new NotImplementedException();
}

#endregion
}

SOAP序列化服务:

public class SoapSerializationFace : ISerializationFace
{
#region ISerializationFace 成员

public string SerializableToString(object target)
{
throw new NotImplementedException();
}

public object DerializableToObject(string target)
{
throw new NotImplementedException();
}

public T Derializable(string target)
{
throw new NotImplementedException();
}

#endregion
}

测试代码如下:

static void Main(string[] args)
{
//外观类
ISerializationFace facede = new SoapSerializationFace();
//测试对象类
Product product = new Product();

//序列化对象
string productString = facede.SerializableToString(product);
//反序列化对象
product = facede.Derializable(productString);
}

这样我们就提高了外观模式的灵活性。

5.2、外观模式的其他考虑

5.2.1-传统外观模式的扩展

上面给出的外观模式,我们在具体的测试代码中还是直接通过new()具体的序列化对象的形式,我们这里可以进行改进,通过工厂模式,或者是通过配

置文件的形式来解耦,一般可能外观类不多的情况下,通过配置文件的方式来进行解耦是不错的选择,而不用提供单独的工厂去创建。

我们可以通过如下的形式来改进上面的方案,例如我们的配置文件的格式定义如下:

<?xml version="1.0" encoding="utf-8" ?>

那么我们看看序列化工厂带示例代码

public class SerializationFactory
{
public static ISerializationFace Create()
{
//配置文件中读取的Type类型
string type = XMLHelper.GetSectionValue(“”);
return (ISerializationFace)Activator.CreateInstance(Type.GetType(type));
}
}

我们来看看具体的测试代码:

static void Main(string[] args)
{
//外观类
ISerializationFace facede = SerializationFactory.Create();
//测试对象类
Product product = new Product();

//序列化对象
string productString = facede.SerializableToString(product);
//反序列化对象
product = facede.Derializable(productString);
}

5.2.2-传统外观模式的扩展

下面给我们来看看另外一种形式的外观模式中的DTO+外观模式的实例实现代码

我们看看DTO的形式:

///
/// DTO(数据传输对象),也可以称之为数据载体
///
public class DTO
{

private Product _product;
private List _orderList;
//全部都是数据信息或者是其他的引用对象信息
public Product Product
{
get
{
return this._product;
}
set
{
this._product = value;
}
}
public List Product
{
get
{
return this._orderList;
}
set
{
this._orderList = value;
}
}

}

具体使用DTO对象的外观类代码如下:

///
/// 远程访问服务
///
public class AccessService
{
bool GetService(DTO dto)
{
return true;
}

DTO GetData()
{
return new DTO();
}
}

通过这样的简易的方式即可完成服务的访问。

程序的测试代码如下:

static void Main(string[] args)
{
//外观类
AccessService service = new AccessService();

DTO dto= service.GetData();
//TODO…
}

通过上面的代码,我们完成了最简单的远程外观的访问服务,远程的外观服务还可以通过WCF的形式来完成,由于WCF内置集成了Remoting和

WebService的形式调用,或者是socket的形式。我这里就不详细的介绍了

六、外观模式使用总结

外观模式作为结构型模式中的一个简单又实用的模式,外观模式通过封装细节来提供大粒度的调用,直接的好处就是,封装细节,提供了应用写程序

的可维护性和易用性。外观模式一般应用在系统架构的服务层中,当我们是多个不同类型的客户端应用程序时,比如一个系统既可以在通过Web的形式访

问,也可以通过客户端应用程序的形式时,可能通过外观模式来提供远程服务,让应用程序进行远程调用,这样通过外观形式提供服务,那么不管是什么样

的客户端都访问一致的外观服务,那么以后就算是我们的应用服务发生变化,那么我们不需要修改没一个客户端应用的调用,只需要修改相应的外观应用即

可。

七、系列进度

创建型

1、系统架构技能之设计模式-单件模式

2、系统架构技能之设计模式-工厂模式

3、系统架构技能之设计模式-抽象工厂模式

4、系统架构技能之设计模式-创建者模式

5、系统架构技能之设计模式-原型模式

结构型

1、系统架构技能之设计模式-组合模式

2、系统架构技能之设计模式-外观模式

3、系统架构技能之设计模式-适配器模式

4、系统架构技能之设计模式-桥模式

5、系统架构技能之设计模式-装饰模式

6、系统架构技能之设计模式-享元模式

7、系统架构技能之设计模式-代理模式

行为型

1、系统架构技能之设计模式-命令模式

2、系统架构技能之设计模式-观察者模式

3、系统架构技能之设计模式-策略模式

4、系统架构技能之设计模式-职责模式

5、系统架构技能之设计模式-模板模式

6、系统架构技能之设计模式-中介者模式

7、系统架构技能之设计模式-解释器模式

八、下篇预告

下篇将会针对外观模式进行讲述,该模式也是结构型模式中很有特点设计模式之一,该 模式是将现有系统中的一些细粒度的东西通过外观对象包装起来,

在应用程序中访问这些方法的时候,通过外观类的形式,提供统一的访问入口,并且具体的细节,应用程序并不需要知道,这样就会降低程序调用的复杂

性,由于本人水平有限,不足或者有错误的地方,请大家批评指正,请大家继续支持我,谢谢。

九、Demo下载

本文Demo

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

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

相关文章

聚观早报|全球AI领域投资总额暴降;哈啰租车开启假期早鸟预定

【聚观365】9月7日消息 全球AI领域投资总额暴降 哈啰租车开启假期早鸟预定 微信上线“腾讯混元助手”小程序 腾讯“QQ群恢复”功能下线 一嗨租车1.1万个直营网点接入滴滴APP 全球AI领域投资总额暴降 媒体报道称&#xff0c;科技巨头Meta&#xff08;原Facebook&#xff…

TCP/IP基础

前言&#xff1a; TCP/IP协议是计算机网络领域中最基本的协议之一&#xff0c;它被广泛应用于互联网和局域网中&#xff0c;实现了不同类型、不同厂家、运行不同操作系统的计算机之间的相互通信。本文将介绍TCP/IP协议栈的层次结构、各层功能以及数据封装过程&#xff0c;帮助您…

js-14---什么是事件代理?应用场景是什么?

1、事件代理是什么&#xff1f; 事件代理&#xff0c;通俗来讲就是把一个元素响应事件&#xff08;click&#xff0c;keydown…&#xff09;的函数委托到另一个元素。 事件流都会经过三个阶段&#xff1a;捕获阶段–>目标阶段----->冒泡阶段 事件委托就是在冒泡阶段完成&…

Fiddler安装与使用教程(2) —— 软测大玩家

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

AI新时代,新华三存储新思考

在数字经济蓬勃发展下&#xff0c;数据正在重塑社会运行模式&#xff0c;推动百行百业的创新发展&#xff0c;成为经济发展的关键驱动力。如今数据成为企业的重要资产&#xff0c;企业希望通过数据了解市场和客户的需求来快速决策&#xff0c;这不仅需要数据高效流转&#xff0…

Python爬取电影信息:Ajax介绍、爬取案例实战 + MongoDB存储

Ajax介绍 Ajax&#xff08;Asynchronous JavaScript and XML&#xff09;是一种用于在Web应用程序中实现异步通信的技术。它允许在不刷新整个网页的情况下&#xff0c;通过在后台与服务器进行数据交换&#xff0c;实时更新网页的一部分。Ajax的主要特点包括&#xff1a; 异步通…

【解决】mysqladmin flush-hosts

问题 mysql出现 mysqladmin flush-hosts&#xff0c;是因为其他客户机连接错误次数过多时&#xff0c;mysql会禁止客户机连接。 解决方法 1、进入服务器数据库&#xff0c;打开数据库命令行界面输入 flush hosts; 此时便可连接 2、可以.修改mysql配置文件&#xff0c;在[…

SpringBoot项目--电脑商城【上传头像】

一、易错点 1.错误写法&#xff1a; 把文件存到数据库中,需要图片时访问数据库,数据库将文件解析为字节流返回,最后写到本地的某一个文件.这种方法太耗费资源和时间了 2.正确写法&#xff1a; 将对应的文件保存在操作系统上,然后再把这个文件路径记录下来,因为在记录路径的…

重生奇迹通关恶魔广场攻略篇

初次进入重生奇迹MU的血色城堡&#xff0c;你可能会对里面不断刷新出来的怪物产生一种密集恐惧症&#xff0c;但是请相信一点&#xff0c;那就是恶魔广场里面的怪物更多&#xff0c;而且品种还不重复&#xff0c;由低至高&#xff0c;轮番刷新&#xff0c;一波又一波……在我看…

Linux部署kettle并设置定时任务

一.安装Kettle linux中使用kettle时首先需要jdk环境&#xff0c;这里就不概述linux中jdk的安装与配置了。 1.首先将kettle压缩包放入linux并解压 unzip data-integration.zip kettle安装路径为:/root/Kettle9.3/data-integration 设置权限 chmod -R 755 /root/Kettle9.3/d…

Allegro画原理图时不能用的非法字符,你知道吗?

Cadence Allegro是一款电子设计自动化工具&#xff0c;常用于原理图绘制和电路设计&#xff0c;在使用Allegro画原理图时&#xff0c;电子工程师可能为了确保文件的准确性和稳定性&#xff0c;能够顺利进行后续的PCB设计和制造&#xff0c;需要注意这些非法字符&#xff0c;那么…

提高 Web 开发效率的10个VS Code扩展插件,你知道吗?

前言 一个出色的开发工具可以显著提高开发人员的开发效率&#xff0c;而优秀的扩展插件则能更进一步地提升工具的效率。在前端开发领域&#xff0c;VSCode毫无疑问是目前最受欢迎的开发工具。为了帮助前端开发人员提高工作效率&#xff0c;今天小编将向大家推荐10个强大的VSCo…

TSINGSEE青犀视频AI智能算法平台电动车入梯检测解决方案

一、方案背景 随着大众的出行要求逐渐提升&#xff0c;交通拥堵现象也随处可见&#xff0c;电动车出行&#xff0c;就成了大家的首选。随着电动车数量的激增&#xff0c;众多用户为了个人方便&#xff0c;大多在室内停放或充电&#xff0c;有的甚至停放在走道、楼梯间等公共区…

无涯教程-JavaScript - BITOR函数

描述 BITOR函数返回两个数字的按位"或"。 语法 BITOR (number1, number2)争论 Argument描述Required/OptionalNumber1Must be in decimal form and greater than or equal to 0.RequiredNumber2Must be in decimal form and greater than or equal to 0.Required…

基于Java+SpringBoot+Vue前后端分离青年公寓服务平台设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

vue基础知识七:SPA首屏加载速度慢的怎么解决?

一、什么是首屏加载 首屏时间&#xff08;First Contentful Paint&#xff09;&#xff0c;指的是浏览器从响应用户输入网址地址&#xff0c;到首屏内容渲染完成的时间&#xff0c;此时整个网页不一定要全部渲染完成&#xff0c;但需要展示当前视窗需要的内容 首屏加载可以说…

指针跃动(济南)客户运营服务中心上线了!

指针跃动&#xff08;济南&#xff09;客户运营服务中心上线了&#xff01; ——打通客户运营服务全链路—— 随着全国代驾业务需求的不断增长&#xff0c;“指针跃动”宣布&#xff1a;指针跃动&#xff08;济南&#xff09;客户运营服务中心上线了&#xff01; 以新的思维方式…

CMT:卷积与Transformers的高效结合

论文提出了一种基于卷积和VIT的混合网络&#xff0c;利用Transformers捕获远程依赖关系&#xff0c;利用cnn提取局部信息。构建了一系列模型cmt&#xff0c;它在准确性和效率方面有更好的权衡。 CMT:体系结构 CMT块由一个局部感知单元(LPU)、一个轻量级多头自注意模块(LMHSA)和…

end value has mixed support, consider using flex-end instead

这是因为 应该用flex-end,不应该用end 所以将所有的都改好之后&#xff0c;就不会再报这个错了

python3 修改nacos的yaml配置

一、安装nacos库 pip install nacos-sdk-python 二、代码如下 import nacos import yaml# 连接地址 NACOS_SERVER_ADDRESSES "192.168.xx.xx" NACOS_SERVER_PORT 替换为你的端口号&#xff0c;如8848# 命名空间 NACOS_NAMESPACE "your_namespace"# 账…