当前位置: 首页 > news >正文

C# 设计模式之组合模式

总目录


前言

日常使用电脑过程中,经常接触到文件夹和文件,我们知道在文件系统中,文件夹可以包含文件夹,且可以嵌套多层,最里面包含的是文件。文件作为最底层对象,里面是不可以再包含文件夹和文件的。类似于文件目录这样的结构,可以称之为树形结构。而树形结构中的对象可以分为两类,一类是:容器对象(复合对象),可以包含其他的子对象;另一类是:叶子对象(简单对象),这类对象是不能在包含其他对象的对象。由于简单对象和复合对象在功能上区别,导致在操作过程中必须区分简单对象和复合对象,这样就会导致客户调用带来不必要的麻烦,作为客户,它们希望能够始终一致地对待简单对象和复合对象。对于这样的问题,让我们看看组合模式是怎样解决这个问题的呢?


1 基础介绍

  1. 组合模式允许你将对象组合成树形结构来表现”部分-整体“的层次结构,使得客户以一致的方式处理单个对象和组合对象

  2. 组合模式实现的最关键的地方是:简单对象和复合对象必须实现相同的接口。这就是组合模式能够将组合对象和简单对象进行一致处理的原因。

  3. 组合模式中的角色:

    • 抽象构件角色(Component):它可以是接口或抽象类,为节点组件和容器组件对象声明接口,在该类中包含共有行为的声明。在抽象组件类中,定义了访问和管理它的子组件的方法(在透明式组合模式中是这样)。在安全式的组合模式中,构件角色并不定义出管理子对象的方法,这一定义由树枝结构对象给出。
    • 树叶构件角色(Leaf):节点对象为最小组件(可以理解为树叶),并继承自抽象构件类,实现其共有声明和方法。
    • 树枝构件角色或称容器构件类(Composite):容器对象可以包含无数节点对象和无数容器组件(可以理解为树枝,可以有无数树叶或者分支),容器对象需要实现管理子对象的方法,如Add、Remove等。

2 使用场景

当发现需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑使用组合模式了。

3 实现方式

组合模式有两种实现方式:

  • 透明式的组合模式
  • 安全式的组合模式。

何为“透明式”,何为“安全式”?

透明式是指“抽象构件角色”定义的接口行为集合包含两个部分,一部分是叶子对象本身所包含的行为(比如Operation),另外一部分是容器对象本身所包含的管理子对象的行为(Add,Remove)。这个抽象构件必须同时包含这两类对象所有的行为,客户端代码才会透明的使用,无论调用容器对象还是叶子对象,接口方法都是一样的,这就是透明,针对客户端代码的透明,但是也有他自己的问题,叶子对象不会包含自己的子对象,为什么要有Add,Remove等类似方法呢,调用叶子对象这样的方法可能(注意:我这里说的是可能,因为有些人会把这些方法实现为空,不做任何动作,当然也不会有异常抛出了,不要抬杠)会抛出异常,这样就不安全了,然后人们就提出了“安全式的组合模式”。所谓安全式是指“抽象构件角色”只定义叶子对象的方法,确切的说这个抽象构件只定义两类对象共有的行为,然后容器对象的方法定义在“树枝构件角色”上,这样叶子对象有叶子对象的方法,容器对象有容器对象的方法,这样责任很明确,当然调用肯定不会抛出异常了。

1. 透明式的组合模式

现在我们分析,在文件系统中存在的文件夹和文件 两个对象,常用的操作无非就是
1 添加文件夹或文件
2 移除文件夹或文件
3 打开文件夹或文件
那么我们对外提供的就是这三个接口,这就是容器对象(复合对象)和叶子对象(简单对象)统一的操作接口。

定义抽象类,规范对外提供的接口,这里就是定义文件系统的抽象类

    //文件系统的抽象类//负责定义容器对象和简单对象统一对象提供的接口public abstract class AbstractFolder {// 添加文件夹或文件public abstract void Add(AbstractFolder folder);//删除文件夹或文件public abstract void Remove(AbstractFolder folder);//打开文件夹或文件public abstract void Open();}

定义叶子对象,实现抽象类的接口,这是就是文件

    public class File : AbstractFolder{//由于文件是叶子对象,是无法再向文件内添加/移除 文件夹或文件的,因此此处通过抛异常处理public override void Add(AbstractFolder folder){throw new Exception("无法向文件中添加文件夹或文件");}public override void Remove(AbstractFolder folder){throw new Exception("无法向文件中移除文件夹或文件");}public override void Open(){Console.WriteLine("打开文件!");}}

定义容器对象,同样实现文件抽象类的接口,这里就是文件夹

    public class Folder : AbstractFolder{public override void Add(AbstractFolder folder){Console.WriteLine("添加文件夹或者文件");}public override void Open(){Console.WriteLine("打开文件价!");}public override void Remove(AbstractFolder folder){Console.WriteLine("移除文件夹或者文件");}}

客户端调用:

        static void Main(string[] args){AbstractFolder file = new File();file.Open();//file.Add(new Folder());//会抛异常//file.Remove(new Folder());//会抛异常AbstractFolder folder = new Folder();folder.Open();folder.Remove(new Folder());folder.Add(new Folder());}

我们发现:文件其实是不需要 Add与Remove 方法的,但是为了保持文件和文件夹一致,对外提供一致的接口,因此文件就一同继承Add与Remove方法,并在方法中做出抛异常的处理。优点是让我们清晰透明的知道其中的实现过程,缺点就是对于客户端的调用并不安全,正因如此就有了安全式的组合模式。

2. 安全式的组合模式

1 定义文件抽象类

	//文件抽象类中不再定义Add和Remove方法//而是只定义,文件夹和文件都有的接口Openpublic abstract class AbstractFolder {//打开文件夹或文件public abstract void Open();}// 文件类,也只需实现一个Open方法即可public class File : AbstractFolder{public override void Open(){Console.WriteLine("打开文件!");}}//将文件夹特有的操作,定义在文件夹实现类中public class Folder : AbstractFolder{public void Add(AbstractFolder folder){Console.WriteLine("添加文件夹或者文件");}public override void Open(){Console.WriteLine("打开文件价!");}public void Remove(AbstractFolder folder){Console.WriteLine("移除文件夹或者文件");}}

客户端调用:

        static void Main(string[] args){AbstractFolder file = new File();file.Open();Folder folder = new Folder();folder.Open();folder.Remove(new Folder());folder.Add(new Folder());}

安全式 组合模式:将文件夹和文件共有的Open 行为,定义在了抽象类中,将文件夹自己需要的Add 和Remove行为定义在文件夹自己的类中!

4 优缺点分析

优点:

  • 组合模式使得客户端代码可以一致地处理单个对象和组合对象
  • 将”客户代码与复杂的对象容器结构“解耦。
  • 可以更容易地往组合对象中加入新的构件。

缺点:

  • 使得设计更加复杂。客户端需要花更多时间理清类之间的层次关系。(这个是几乎所有设计模式所面临的问题)。

结语

希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。


参考资料:
C#设计模式(10)——组合模式(Composite Pattern)
C#设计模式之九组合模式(Composite Pattern)【结构型】
c#中组合模式详解

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • [flink]部署模式
  • 【网络安全】玲珑安全第四期
  • 打卡第34天------动态规划-01背包
  • 【practise】数组中出现次数超过一半的数字
  • ABAP小白开发操作手册+(九)ABAP调用http
  • 泛型类型,存在确定得属性,剩下的属性都是通过泛型传进来
  • 【数据结构的——红黑树】
  • Javascript——宏任务微任务与JavaScript引擎的事件循环(Event Loop)和任务调度
  • C语言求平方和倒数
  • 【区块链+社会公益】第一反应互助急救链 | FISCO BCOS应用案例
  • leetcode50. Pow(x, n),快速幂算法
  • Java 代码详解:删除链表中的倒数第 N 个节点
  • JavaScript 数组迭代
  • WPF篇(5)- Border控件(边框布局)+GridSplitter分割窗口
  • Linux虚拟化技术的演进:Xen与KVM的历程与影响
  • Android Studio:GIT提交项目到远程仓库
  • java小心机(3)| 浅析finalize()
  • JAVA之继承和多态
  • linux安装openssl、swoole等扩展的具体步骤
  • PHP 小技巧
  • PHP面试之三:MySQL数据库
  • Python 使用 Tornado 框架实现 WebHook 自动部署 Git 项目
  • TypeScript迭代器
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • Vue组件定义
  • 分类模型——Logistics Regression
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 入手阿里云新服务器的部署NODE
  • 使用 Node.js 的 nodemailer 模块发送邮件(支持 QQ、163 等、支持附件)
  • 一起参Ember.js讨论、问答社区。
  • Redis4.x新特性 -- 萌萌的MEMORY DOCTOR
  • ​1:1公有云能力整体输出,腾讯云“七剑”下云端
  • # Panda3d 碰撞检测系统介绍
  • # Pytorch 中可以直接调用的Loss Functions总结:
  • #Spring-boot高级
  • #window11设置系统变量#
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • #我与Java虚拟机的故事#连载08:书读百遍其义自见
  • (13)Hive调优——动态分区导致的小文件问题
  • (C++20) consteval立即函数
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (附源码)计算机毕业设计ssm高校《大学语文》课程作业在线管理系统
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (精确度,召回率,真阳性,假阳性)ACC、敏感性、特异性等 ROC指标
  • (十一)c52学习之旅-动态数码管
  • (实测可用)(3)Git的使用——RT Thread Stdio添加的软件包,github与gitee冲突造成无法上传文件到gitee
  • (实战)静默dbca安装创建数据库 --参数说明+举例
  • (一一四)第九章编程练习
  • (已解决)vue+element-ui实现个人中心,仿照原神
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • (转)Linq学习笔记
  • (转载)OpenStack Hacker养成指南
  • .gitignore不生效的解决方案
  • .Net Core中的内存缓存实现——Redis及MemoryCache(2个可选)方案的实现