快速入门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://www.rhkb.cn/news/384340.html

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

相关文章

Pytorch深度学习实践(5)逻辑回归

逻辑回归 逻辑回归主要是解决分类问题 回归任务:结果是一个连续的实数分类任务:结果是一个离散的值 分类任务不能直接使用回归去预测,比如在手写识别中(识别手写 0 − − 9 0 -- 9 0−−9),因为各个类别…

CentOS7下操作iptables防火墙和firewalld防火墙

CentOS7下操作iptables防火墙和firewalld防火墙 💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、…

【OpenCV C++20 学习笔记】调节图片对比度和亮度(像素变换)

调节图片对比度和亮度(像素变换) 原理像素变换亮度和对比度调整 代码实现更简便的方法结果展示 γ \gamma γ校正及其实操案例线性变换的缺点 γ \gamma γ校正低曝光图片矫正案例代码实现 原理 关于OpenCV的配置和基础用法,请参阅本专栏的其…

HAL STM32 SPI/ABZ/PWM方式读取MT6816磁编码器数据

HAL STM32 SPI/ABZ/PWM方式读取MT6816磁编码器数据 📚MT6816相关资料(来自商家的相关资料): 资料:https://pan.baidu.com/s/1CAbdLBRi2dmL4D7cFve1XA?pwd8888 提取码:8888📍驱动代码编写&…

FastAPI(七十九)实战开发《在线课程学习系统》接口开发-- 加入课程和退出课程

源码见:"fastapi_study_road-learning_system_online_courses: fastapi框架实战之--在线课程学习系统" 加入课程 我们先看下加入课程 1.是否登录 2.课程是否存在 3.是否已经存在 4.添加 首先实现逻辑 def get_student_course(db: Session, course: int…

如何开启或者关闭 Windows 安全登录?

什么是安全登录 什么是 Windows 安全登录呢?安全登录是 Windows 附加的一个组件,它可以在用户需要登录的之前先将登录界面隐藏,只有当用户按下 CtrlAltDelete 之后才出现登录屏幕,这样可以防止那些模拟登录界面的程序获取密码信息…

【9.PIE-Engine案例——加载Terra星全球500m植被指数16天合成产品(MOD13A1 V61)数据集】

加载Terra星全球500m植被指数16天合成产品(MOD13A1 V61)数据集 原始路径 欢迎大家登录航天宏图官网查看本案例原始来源 最终结果 具体代码 /*** File : MOD13A1* Time : 2020/7/21* Author : piesat* Version : 1.0* Contact : 400-890-0662* License : …

Interesting bug caused by getattr

题意:由 getattr 引起的有趣的 bug 问题背景: I try to train 8 CNN models with the same structures simultaneously. After training a model on a batch, I need to synchronize the weights of the feature extraction layers in other 7 models. …

WARNING: Ignoring invalid distribution -ip警告信息如何去掉?

查看已安装依赖列表的时候,出现了很多警告信息,如何去掉呢? 解决办法 打开这个路径:d:\software\python\python39\lib\site-packages 这种波浪线开头的,我们将它删除掉,就可以了。

Ubuntu设置网络

进入网络配置文件夹 cd /etc/netplan 使用 vim 打开下的配置文件 打开后的配置 配置说明: network:# 网络配置部分ethernets:# 配置名为ens33的以太网接口ens33:addresses:# 为ens33接口分配IP地址192.168.220.30,子网掩码为24位- 192.168.220.30/24n…

VS2019报错:找不到导入的项目,请确认import声明

解决办法 找到项目的.vcxproj文件 用记事本打开后使用ctrlF搜索import 发现import Project后面的.props文件路径不对,将路径改为相对路径 保存后重新加载项目,即可生成成功

AI发展下的伦理挑战:构建未来科技的道德框架

一、引言 随着人工智能(AI)技术的飞速发展,我们正处在一个前所未有的科技变革时代。AI不仅在医疗、教育、金融、交通等领域展现出巨大的应用潜力,也在日常生活中扮演着越来越重要的角色。然而,这一技术的迅猛进步也带来…

git实践汇总【配置+日常使用+问题解决】

**最初配置步骤:** git config --global user.name "yournemae" git config --global user.email "yourmail" git config -l ssh-keygen -t rsa -C “xxx.xxxx.EXTcccc.com” git config --global ssh.variant ssh $ git clone git仓库路径 git…

云盘高速检测的秘密:密封圈外观检测全解析!

密封圈是一种用于填塞、隔离或密封两个相互连接部件之间空隙的圆形密封装置。密封圈通常由橡胶、塑料、金属等材料制成,具有弹性并能在压力作用下填充间隙,防止液体、气体或固体物质泄漏。 密封圈可根据具体应用选择不同材料,如橡胶密封圈适…

「Unity3D」场景中的距离单位Unit与相关设置PixelsToUnits、PixelsPerUnit

GameObject在场景的位置Position,并没有明确是什么具体单位——如:Transform的x、y、z,或RectTransform的PosX、PosY、PosZ。而RectTransform在面板上显示的Width和Height,也没有具体单位,其实并不是像素。 事实上&am…

谷粒商城实战笔记-59-商品服务-API-品牌管理-使用逆向工程的前后端代码

文章目录 一, 使用逆向工程生成的代码二,生成品牌管理菜单三,几个小问题 在本次的技术实践中,我们利用逆向工程的方法成功地为后台管理系统增加了品牌管理功能。这种开发方式不仅能快速地构建起功能模块,还能在一定程度…

qemu上运行android-x86 (基于ubuntu)

安装qemu x86_64 下载qemu源代码 进入根目录,执行./configure --target-listx86_64-softmmu make -j4 sudo make install 期间可能遇到python相关问题,比如版本不对、库找不到,版本不对可自行安装或是修改环境变量,库找不到可以检…

如何检查代理IP地址是否被占用

使用代理IP时,有时候会发现IP仍然不可用,可能是因为已经被其他用户或者网络占用了。为了检测代理IP是否被占用,我们可以采用一些方法进行验证测试,以保证代理IP的有效性和稳定性。 1.ARP缓存方法 ARP缓存法是一种简单有效的检测代…

unity 实现图片的放大与缩小(根据鼠标位置拉伸放缩)

1创建UnityHelper.cs using UnityEngine.Events; using UnityEngine.EventSystems;public class UnityHelper {/// <summary>/// 简化向EventTrigger组件添加事件的操作。/// </summary>/// <param name"_eventTrigger">要添加事件监听的UI元素上…

将Android Library项目发布到JitPack仓库

将项目代码导入Github 1.将本地项目目录初始化为 Git 仓库。 默认情况下&#xff0c;初始分支称为 main; 如果使用 Git 2.28.0 或更高版本&#xff0c;则可以使用 -b 设置默认分支的名称。 git init -b main 如果使用 Git 2.27.1 或更低版本&#xff0c;则可以使用 git symbo…