快速入门C#设计模式【2】结构型模式

结构型模式

  1. 适配器模式 (Adapter)

  2. 桥接模式 (Bridge)

  3. 组合模式 (Composite)

  4. 装饰模式 (Decorator)

  5. 外观模式 (Facade)

  6. 享元模式 (Flyweight)

  7. 代理模式 (Proxy)

适配器模式(Adapter Pattern)

适配器模式(Adapter Pattern)是一种结构型设计模式,它允许不兼容的接口之间可以一起工作。这种模式通常用于系统后期维护和扩展过程中,帮助已存在的系统与第三方库、API 或者是遗留系统进行交互,而无需修改原有代码。

适配器模式的组成

适配器模式通常包括以下几个组件:

  • 目标(Target):定义客户期望使用的特定域相关接口。

  • 客户(Client):与符合目标接口的对象协作。

  • 被适配者(Adaptee):一个已经存在的接口,需要适配。这个接口需要被转换,因为它的设计不能满足目标接口的需求。

  • 适配器(Adapter):适配器把原接口转换为目标接口。

示例:电源适配器

假设有一个简单的场景:一个美国制造的电器只能接受120伏的电压,而欧洲的标准电压是230伏。我们需要一个电源适配器来使这个电器能在欧洲正常工作。

首先,定义目标接口,即客户希望使用的接口:

58e1b42b27c0d21546bffb1c296e27ca.png

然后是已存在的类,即被适配者:

c8378bd9b44bfa6db87d611499b3e548.png

接着,实现适配器类,使其兼容目标接口:

227697e65a8391106f78ba47c817db17.png

最后,客户端代码演示如何使用适配器:

6e52751c5dfe5d0acf5863d6bf983952.png

在这个示例中,SocketAdapter 类通过将 AmericanSocket 类包装进一个实现了 IEuropeanSocket 接口的适配器中,使得原本只能接受120伏电压的美国电器可以在230伏的欧洲电压下安全工作。适配器负责接口之间的转换和兼容性处理,客户端代码则可以保持不变,依然使用期望的接口进行操作。这样的设计增强了代码的可维护性和扩展性.

桥接模式(Bridge Pattern)

桥接模式(Bridge Pattern)是一种结构型设计模式,用于将抽象与其实现分离,使得两者可以独立地变化。这种模式通过提供一个桥接结构,使得抽象部分的代码可以与接口实现部分的代码分离,从而减少彼此间的依赖关系。

桥接模式的组成

桥接模式通常包含以下几个部分:

  • 抽象(Abstraction):定义抽象类的接口。它持有一个对实现部分对象的引用,即实现(Implementor)。

  • 扩展抽象(Refined Abstraction):扩展抽象类从抽象类派生,并实现其中定义的抽象方法。

  • 实现者(Implementor):定义实现类的接口,这个接口不需要与抽象类的接口完全一致;事实上,这两个接口可以完全不同。

  • 具体实现(Concrete Implementor):实现 Implementor 接口并定义其具体实现的类。

示例:遥控器和电视

假设我们要设计一个遥控器系统,不同品牌的电视可以通过不同类型的遥控器进行控制。遥控器就是抽象部分,而电视就是实现部分。

1. 实现者接口(Implementor)

622b755be8588ca9c0726517b9d99cd8.png

2. 具体实现(Concrete Implementor)

为两种品牌的电视实现上述接口:

5fb6f61b1f7cceea0bc253c73596014b.png

3. 抽象(Abstraction)

定义遥控器的抽象类:

50133a41c277a3f8b8e23ff6c53415fb.png

4. 扩展抽象(Refined Abstraction)

实现具体的遥控器类:

41e247f93f7dd114d5ea2419fd3fbc13.png

5. 客户端使用

6c28ab84649a800a40a7969e7cb089a7.png

桥接模式使得抽象部分和实现部分可以独立扩展,不仅提高了系统的灵活性,还使得代码更易于维护。在这个例子中,添加一个新品牌的电视或者改变遥控器的设计不会影响到另一方,这正是桥接模式所提供的优势。

组合模式(Composite Pattern)

组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构以表示部分-整体的层次结构。组合模式使得客户端对单个对象和组合对象的使用具有一致性。

组合模式的组成

组合模式主要包括以下几个角色:

  • 组件(Component):这是一个抽象角色,为组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理子部件。

  • 叶节点(Leaf):在组合中表示叶节点对象,叶节点没有子节点。

  • 复合组件(Composite):定义有子部件的那些部件的行为。存储子部件,并实现与子部件有关的操作。

示例:文件系统

在这个示例中,我们将使用组合模式来构建一个简单的文件系统,包括文件和文件夹。

1. 组件接口

67ad21e603dfe31776dbec16800c7f42.png

2. 叶节点(Leaf)

9b8d6f4f60043c986f9dc2cb1249cfe7.png

3. 复合组件(Composite)

2d8bd56638bcc99fa48b02bf3417f348.png

4. 客户端使用

使用以及执行结果:

b7333fcb34c7fb6fc4cc06ffe849a5f7.png

在这个例子中,Directory 类(复合组件)可以包含其他 Directory 对象或 File 对象(叶节点),形成一个树形结构。每个组件都实现了 FileSystemComponent 接口,这使得客户端在处理文件和文件夹时可以具有一致的方式。通过调用 Display 方法,可以展示整个文件系统的结构,展示每个文件和文件夹的层次。这种设计使得添加或删除新的文件类型或文件夹时,对其他代码的影响最小,体现了组合模式的优势,即“使用户对单个对象和组合对象的使用具有一致性”。

装饰模式(Decorator Pattern)

装饰模式(Decorator Pattern)是一种结构型设计模式,它允许用户在不修改现有对象的结构的情况下,向对象添加新的功能。装饰模式通过创建一个包含目标对象的包装对象来实现新功能的添加,这样既扩展了对象的功能,也遵守了开闭原则(对扩展开放,对修改关闭)。

装饰模式的组成

装饰模式主要涉及以下几个角色:

  • 抽象组件(Component):定义一个对象接口,可以给这些对象动态地添加职责。

  • 具体组件(ConcreteComponent):定义了一个具体的对象,也可以给这个对象添加一些职责。

  • 装饰角色(Decorator):持有一个组件(Component)对象的引用,并定义了一个与组件接口一致的接口。

  • 具体装饰(ConcreteDecorator):具体实现装饰角色提供的装饰。

示例:咖啡店

在这个例子中,我们将使用装饰模式来模拟咖啡店的订单系统,其中顾客可以选择不同类型的咖啡,并可添加多种调料。

1. 抽象组件(Component)

1c9c9dcc3d1f32dc799a00bdbf755a0a.png

2. 具体组件(ConcreteComponent)

定义几种咖啡:

32e2d3a6c03c1b91f403ecb9f4233597.png

3. 装饰角色(Decorator)

435dae5227f7586597f7ad9e23257269.png

4. 具体装饰(ConcreteDecorator)

定义几种调料装饰:

1b835792fac6089178e2e711ce8c7597.png

5. 客户端使用

实际调用和结果输出如下

05dbca2e06852d322add4cfd580834f3.png

在这个示例中,Espresso 和 HouseBlend 是具体的咖啡,分别实现了 Beverage 抽象类。Mocha 和 Hand 是装饰者,它们继承自 CondimentDecorator,这是一个抽象装饰类。每个装饰者类增加了额外的行为(添加调料)并调整了价格。通过装饰者,可以灵活地添加或修改对象的行为,同时保持代码的简洁和可维护性。这种方式非常适合于功能频繁变化的系统。

外观模式(Facade Pattern)

外观模式(Facade Pattern)是一种常用的软件设计模式,它提供了一个高层次的接口,使得系统更加容易使用。外观模式常常用于为复杂的系统或库提供一个简单的接口,减少系统间的依赖,增加子系统的独立性和可移植性。

设计目的

外观模式的主要目的是隐藏系统的复杂性,提供一个简化的接口给客户端。通过这种方式,如果后续系统内部发生变化,客户端代码不需要改动;只需要在外观类中调整即可。

使用场景

  • 系统有很多分散的类,操作起来很复杂,需要一个简化的接口。

  • 客户端与多个子系统之间存在很大的依赖性,引入外观模式将这些子系统封装起来,提高子系统的独立性和安全性。

  • 在层次化结构中,可以使用外观模式定义系统的每一层的入口。

实现步骤

  1. 确定要简化的子系统的功能集。

  2. 创建一个外观类,它将负责调用子系统的方法,处理客户端的请求。

  3. 客户端通过外观类与子系统交互,降低了系统的复杂性。

示例

假设有一个复杂的音频系统,包含了多个组件,如音量控制、信号处理、音频播放等。我们可以创建一个外观类,来简化和统一这些操作。

C#代码实现

首先,定义一些子系统类:

2c17b3e13504b4525e356599d7021544.png

接下来,创建外观类:

211f80886cd1a6a55ce7a8e2558d6c25.png

最后,使用方式以及运行效果:

967000738f1188a478382acd155e8d19.png

在上述例子中,AudioFacade 类提供了一个简单的接口 PlaySound,客户端通过这个接口可以很容易地播放音频而不需要直接与复杂的子系统交互。这样,客户端代码变得更简单,同时也增强了系统各部分之间的独立性。

享元模式(Flyweight Pattern)

享元模式(Flyweight Pattern)是一种用于性能优化的结构型设计模式。这个模式通过共享尽可能多的相似对象来减少内存使用,特别适用于处理大量对象时,其中许多对象由重复的状态组成。

设计目的

享元模式的主要目的是在有大量相似对象的情况下,通过共享尽可能多的对象以减少内存消耗。这通常是通过将这些对象的状态分为“内部状态”(intrinsic)和“外部状态”(extrinsic)来实现的:

  • 内部状态是存储在享元对象内部的,不会随环境改变而改变。

  • 外部状态是根据场景外部变化的,并需要客户端提供。

使用场景

  • 当一个程序使用了大量的对象,这些对象大部分状态可以被外部化时。

  • 当因为使用大量对象,造成很高的内存开销时。

  • 当对象的多数状态都可以变为可共享的时,可以将这些对象替换为少量的共享对象。

示例

假设有一个文档编辑器,它可以设置字符的样式。每个字符可以是一个对象,但是样式(如字体、大小)很可能在多个字符中是相同的。这里,样式可以作为内部状态,由享元对象共享,而每个字符的位置可以作为外部状态由客户端代码来管理。

C#代码实现

首先,定义享元类及接口:

34be29b9eaa9232719e0e3862edac42f.png

然后,创建享元工厂:

1c28234dc7dbe8f7150623aa3f65c8e3.png

具体使用和运行结果如下:

78d8c14b1ae3adb21bab9bc6dbb1e336.png

代理模式(Proxy Pattern)

代理模式(Proxy Pattern)是一种结构型设计模式,它通过提供一个替代品或代表其他对象来控制对这个对象的访问。代理模式可以用于多种情景,例如延迟初始化、访问控制、日志记录和智能引用等。它主要用于控制和管理对象的访问。

设计目的

代理模式的主要目的是:

  • 控制对某个对象的访问。

  • 延迟对象的创建和初始化,从而优化资源和内存的使用。

  • 作为调用方和实际对象之间的中介,添加额外的功能,如安全检查、日志等。

使用场景

  • 当需要为一个昂贵的操作提供一个轻量级的处理接口时,如对资源的密集型访问。

  • 当需要对原始对象进行访问控制时,提供额外的安全层。

  • 在需要管理生命周期或结果缓存时。

示例

假设有一个文档加载和显示系统,我们想通过使用代理模式来控制对文档的访问,假设加载文档是一个资源密集的操作。

C#代码实现

首先,定义一个文档接口和实现该接口的实际类:

768ebbc0165342b02f94e5f7216f472c.png

接着,创建代理类:

f74e0625c75a4fc59e2f846d668854c9.png

使用方式和运行效果如下:

e03f82f542f024a5cce7dba252f487d7.png

以上就是结构型设计模式的所有演示内容,感兴趣可以上gitee获取以上测试的源码:

https://gitee.com/dreamer_j/design-patterns.git

如果以上内容对你有帮助,欢迎关注、点赞和转发。快捷关注二维码:

d1a67b32940592be30fede3bd34e711c.jpeg

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

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

相关文章

Stable Diffusion 使用详解(3)---- ControlNet

背景 炼丹师在AI绘画的过程中,由于Stable Diffusion的原理是水滴式的扩散作图原理,其实在前面也有提到,他的发挥是‘不稳定’的,因为你没有办法做到精确控制,只能说是大致符合你的预期。你不能总依赖抽卡固定随机数种…

后端采用SpringBoot框架开发的:ADR药物不良反应智能监测系统源码,用于监测和收集药品在使用过程中发生的不良反应的系统

ADR药物不良反应智能监测系统是一套用于监测和收集药品在使用过程中发生的不良反应(Adverse Drug Reaction, ADR)的系统。该系统基于医院临床数据中心,运用信息技术实现药品不良反应的智能监测、报告管理、知识库查询、统计分析等功能&#x…

昇思25天学习打卡营第22天|CV-Vision Transformer图像分类

打卡 目录 打卡 ViT简介 模型结构 基于ViT实现ImageNet分类任务 环境准备与数据读取 模型解析 Transformer基本原理 Self-Attention模块 代码实现 Transformer Encoder 代码实现 ViT模型的输入 Patch Embedding代码处理输入 整体构建ViT 模型训练与推理 模型训…

通过ATS软件抓取ios手机日志方法记录

1.ios手机下载描述符文件,用于过检测 下载网址:https://developer.apple.com/bug-reporting/profiles-and-logs/?nameB 点击这个下载,之后在手机通用-VPN与设备管理里面找到刚才下载的描述文件然后安装; 2024.6月后注意会提示描…

能链智电的危与机:持续亏损、股价崩塌,CEO王阳如何寻求出路?

近日,能链智电(NASDAQ:NAAS)发布未经审计的2024年二季度及上半年财报。数据显示,该公司上半年营收增速稳健,核心充电服务的规模效应得到释放。 能链智电在财报中透露,该公司于6月首度实现了单月…

echarts使用案例

1.配置legend icon 根据点击事件动态更换样式 <template><div ref"chart" style"width: 600px; height: 400px;"></div></template><script>import * as echarts from echarts;export default {name: EchartsExample,data(…

图论理论基础

图论理论基础 | 代码随想录 图的基本概念 二维坐标中&#xff0c;多个点连成的线就构成了图。图也可以是一个节点&#xff0c;甚至没有节点&#xff08;空图&#xff09;。 图的种类 整体上一般分为有向图和无向图。 有向图是指图中边是有方向的&#xff0c;无向图是指图中…

《GPT-4o mini:开启开发与创新的新纪元》

在科技发展的快速进程中&#xff0c;OpenAI 推出的 GPT-4o mini 模型如同一阵春风&#xff0c;给开发者们带来了新的希望和机遇。它以其卓越的性能和极具吸引力的价格&#xff0c;成为了行业内热议的焦点。 当我首次听闻 GPT-4o mini 的消息时&#xff0c;内心充满了好奇与期待…

Pytorch笔记1

建议点赞收藏关注&#xff01;持续更新至pytorch大部分内容更完。 整体框架如下 目录 gpu加速数据数据结构张量TensorVariable 预处理数据增强 模型构建模块组织复杂网络初始化网络参数定义网络层 损失函数创建损失函数设置损失函数超参数选择损失函数 优化器管理模型参数管理…

【ESP32 IDF 软件模拟SPI驱动 W25Q64存储与读取数组】

目录 SPISPI介绍SPI时序代码编写&#xff08;spi&w25q64&#xff09; 代码调试 SPI SPI介绍 SPI&#xff08;Serial Peripheral Interface&#xff0c;串行外围设备接口&#xff09;是一种高速、全双工、同步的串行通信总线&#xff0c;常用于微控制器与各种外围设备&…

【React】详解如何获取 DOM 元素

文章目录 一、基础概念1. 什么是DOM&#xff1f;2. 为什么需要获取DOM&#xff1f; 二、使用 ref 获取DOM元素1. 基本概念2. 类组件中的 ref3. 函数组件中的 ref 三、 ref 的进阶用法1. 动态设置 ref2. ref 与函数组件的结合 四、处理特殊情况1. 多个 ref 的处理2. ref 与条件渲…

大数据-49 Redis 缓存问题中 穿透、雪崩、击穿、数据不一致、HotKey、BigKey

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

nginx目录列表美化—rpm安装

目录美化 1. 下载NGINX2. 下载美化工具3. 配置模块4. 主题下载5. 配置文件编写6. 其它问题 1. 下载NGINX RHEL系列的yum源 使用yum源安装如果不能指定版本&#xff0c;请点击跳转nginx的仓库 nginx-stable] namenginx stable repo baseurlhttp://nginx.org/packages/centos/$…

【H.264】H.264详解(二)—— H264视频码流解析示例源码

文章目录 一、前言二、示例源码【1】目录结构【2】Makefile源码【3】h264parser.c源码【4】编译运行【5】源码下载地址 声明&#xff1a;此篇示例源码非原创&#xff0c;原作者雷霄骅。雷霄骅&#xff0c;中国传媒大学通信与信息系统专业博士生&#xff0c;在此向雷霄骅雷神致敬…

【Python机器学习】朴素贝叶斯——条件概率

条件概率 假设现在有一个装了7块石头的罐子&#xff08;3块灰色&#xff0c;4块黑色&#xff09;&#xff0c;如果从中随机取出一块&#xff0c;灰色的可能性就是3/7&#xff0c;黑色的可能性是4/7。我们使用p(gray)来表示取到灰色石头的概率&#xff0c;其概率值可以通过灰色…

cocos creator 3学习记录01——如何替换图片

一、动态加载本地图片 1、通过将图片关联到CCClass属性上来进行代码切换。 1、这种方法&#xff0c;需要提前在脚本文件中声明好代表图片的CCClass属性。 2、然后拖动图片资源&#xff0c;到脚本内声明好的属性上以进行关联。 3、然后通过程序&#xff0c;来进行切换展示。…

unity2D游戏开发01项目搭建

1新建项目 选择2d模板,设置项目名称和存储位置 在Hierarchy面板右击&#xff0c;create Empty 添加组件 在Project视图中右键新建文件夹 将图片资源拖进来&#xff08;图片资源在我的下载里面&#xff09; 点击Player 修改属性&#xff0c;修好如下 点击Sprite Editor 选择第二…

Hadoop3:HDFS的客户端工具Big Data Tools(IDEA版本)

1、安装插件 在Plugins里搜索Big Data Tools 安装完成后&#xff0c;重启IDEA 2、配置Windows环境 主要是配置Hadoop环境&#xff0c;否则无法通过插件远程连接HDFS 1、解压hadoop安装包 2、进入hadoop的bin目录 放入图中标红的两个文件 3、配置hadoop环境变量 新建HAD…

freertos的学习cubemx版

HAL 库的freertos 1 实时 2 任务->线程 3 移植 CMSIS_V2 V1版本 NVIC配置全部是抢占优先级 第四组 抢占级别有 0-15 编码规则&#xff0c; 变量名 &#xff1a;类型前缀&#xff0c; c - char S - int16_t L - int32_t U - unsigned Uc - uint8_t Us - uint…

【游戏制作】使用Python创建一个完整的2048游戏项目

目录 项目运行展示 项目概述 项目目标 项目结构 安装依赖 代码实现 1. 导入库 2. 创建 Game2048 类 3. 设置UI界面 4. 加载二维码图片 5. 创建菜单 6. 游戏逻辑和功能 7. 运行应用 总结 创建一个完整的2048游戏项目 项目运行展示 项目概述 在这个项目中&#xff…