登录
/
注册
首页
论坛
其它
首页
科技
业界
安全
程序
广播
Follow
关于
每日签到
每天签到奖励2圆-6圆
发帖说明
VIP申请
登录
/
注册
账号
自动登录
找回密码
密码
登录
立即注册
搜索
搜索
关闭
CSDN热搜
程序园
精品问答
技术交流
资源下载
本版
帖子
用户
软件
问答
教程
代码
写记录
写博客
VIP申请
VIP网盘
网盘
联系我们
每日签到
道具
勋章
任务
设置
我的收藏
退出
腾讯QQ
微信登录
返回列表
首页
›
业界区
›
业界
›
c#组合模式详解
c#组合模式详解
[ 复制链接 ]
账暴
2025-6-9 08:20:36
一、基础介绍:
组合模式用于表示
部分-整体
的
层次结构
。适用于希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象的情况。
顾名思义,什么叫部分-整体,比如常见的前端UI,一个DIV标签中可以存在多个A标签、P标签、DIV标签等等。
相较于DIV这个容器整体而言,其中所含的A标签、P标签甚至是DIV标签都是单个的部分。
而显示的时候却是一视同仁,不分部分还是整体。
这就是典型的组合模式。
再比如WinForms应用程序中,Label、TextBox等这样简单的控件,可以理解为节点对象,它们中无法再插入其他控件,它们就是最小的。
而比如GroupBox、DataGrid这样由多个简单控件组成的复合控件或者容器,就可以理解为容器对象,它们中可以再插入其他的节点对象,甚至是再插入其他容器对象。
但不管是Label这种节点对象还是DataGrid这种容器对象,想要显示的话都需要执行OnPaint方法。
为了表示这种对象之间整体与部分的层次结构,System.Windows.Forms.Control类就是应用了这种组合模式。
这样就可以简单的把组合模式分为三个部分:
抽象组件类(Component)
:它可以是接口或抽象类,为节点组件和容器组件对象声明接口,在该类中包含共有行为的声明。在抽象组件类中,定义了访问及管理它的子组件的方法。
节点组件类(Leaf)
:节点对象为最小组件(可以理解为树叶),并继承自抽象组件类,实现其共有声明和方法。
容器组件类(Composite)
:容器对象可以包含无数节点对象和无数容器组件(可以理解为树枝,可以有无数树叶或者分支),容器对象需要实现管理子对象的方法,如Add、Remove等。
二、应用场景:
当发现需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑使用组合模式了
。
UI的一系列控件就是使用了组合模式,整体和部分可以被一致对待。
组合模式有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
以下情况下适用Composite模式:
1.对象的部分-整体层次结构。
2.忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象。
三、创建方式:
组合模式实现的最关键的地方是——简单对象和复合对象必须实现相同的接口。这就是组合模式能够将组合对象和简单对象进行一致处理的原因。
组合模式有两种实现方式,一种是:透明式的组合模式,另外一种是:安全式的组合模式。
透明方式————————————————
Leaf叶类中也有Add 与 Remove方法,这种方式叫透明方式。
也就是说在Component中声明所有用来管理子对象的方法,其中包括Add、Remove等。
这样实现Component接口的所有子类都具备了Add与Remove。
这样做的好处是叶节点和枝节点对于外界没有区别,它们具有一致的行为接口。
但问题也很明显,因为Leaf类本身不具备Add、Remove方法的功能,其实现是没有意义的。
安全方式————————————————
在Component接口中不去声明Add与Remove方法,那么子类Leaf也就不用必须实现它们,而在Composite类中声明所有用来管理子类对象的方法。
以文档管理器为例,文件夹为
Composite
,各类文档为
Leaf
。
透明方式
1).抽象类
1 /// <summary>
2 /// 抽象组件类(Component)
3 /// </summary>
4 public abstract class DocumentComponent
5 {
6 public string Name { get; set; }
7 protected List<DocumentComponent> mChildren;
8 public List<DocumentComponent> Children
9 {
10 get { return mChildren; }
11 }
12 public DocumentComponent(string name)
13 {
14 this.Name = name;
15 mChildren = new List<DocumentComponent>();
16 }
17
18
19 public abstract void AddChild(DocumentComponent document);
20
21 public abstract void RemoveChild(DocumentComponent document);
22
23 public abstract void Show();
24 }
复制代码
接口或抽象类,为节点组件和容器组件对象声明接口,在该类中包含共有行为的声明。
在抽象组件类中,定义了访问及管理它的子组件的方法。
本实例中Show为节点和容器组件共有方法,AddChild和RemoveChild为容器组件方法。
本类主要是为了让节点类和容器类进行继承方便
统一管理
。
2).节点组件类
1 /// <summary>
2 /// 节点组件类(Leaf),各类文档,每类型可以添加一个对应类。
3 /// </summary>
4 public sealed class Word : DocumentComponent
5 {
6 public Word(string name)
7 : base(name)
8 { }
9 public override void AddChild(DocumentComponent document)
10 {
11 throw new Exception("节点类不支持");
12 }
13
14 public override void RemoveChild(DocumentComponent document)
15 {
16 throw new Exception("节点类不支持");
17 }
18
19 public override void Show()
20 {
21 Console.WriteLine("这是一篇word文档:" + Name);
22 }
23 }
复制代码
节点对象为最小组件(可以理解为树叶),并继承自抽象组件类,实现show方法。
AddChild和RemoveChild为容器组件方法,在节点类中抛出异常即可。
该类是
最小单位
,没有子节点。
本类一个word文档对象,如果有多个类型的文档,可以声明多个类。
3).容器组件类
1 /// <summary>
2 /// 容器组件类(Composite),文件夹
3 /// </summary>
4 public class Folder : DocumentComponent
5 {
6 public Folder(string name)
7 : base(name)
8 { }
9 public override void AddChild(DocumentComponent document)
10 {
11 mChildren.Add(document);
12 Console.WriteLine("文档或文件夹增加成功");
13 }
14 public override void RemoveChild(DocumentComponent document)
15 {
16 mChildren.Remove(document);
17 Console.WriteLine("文档或文件夹删除成功");
18 }
19 public override void Show()
20 {
21 Console.WriteLine("这是一个文件夹:" + Name);
22 }
23 }
复制代码
容器对象可以包含无数节点对象和无数容器组件(可以理解为树枝,可以有无数树叶或者分支),容器对象需要实现管理子对象的方法,如AddChild、RemoveChild等。
本类是一个文件夹对象。
4).客户端
1 /// <summary>
2 /// 客户端
3 /// </summary>
4 class Client
5 {
6 /// <summary>
7 /// 广度优先检索
8 /// </summary>
9 /// <param name="component"></param>
10 private static void BreadthFirstSearch(DocumentComponent component)
11 {
12 Queue<DocumentComponent> q = new Queue<DocumentComponent>();
13 q.Enqueue(component);
14 Console.WriteLine(component.Name);
15 while (q.Count > 0)
16 {
17 DocumentComponent temp = q.Dequeue();
18 List<DocumentComponent> children = temp.Children;
19 foreach (DocumentComponent child in children)
20 {
21 Console.WriteLine(child.Name);
22 q.Enqueue(child);
23 }
24 }
25 }
26
27 /// <summary>
28 /// 深度优先检索
29 /// </summary>
30 /// <param name="component"></param>
31 private static void DepthFirstSearch(DocumentComponent component)
32 {
33 Console.WriteLine(component.Name);
34 List<DocumentComponent> children = component.Children;
35 if (children == null || children.Count == 0) return;
36 foreach (DocumentComponent child in children)
37 {
38 DepthFirstSearch(child);
39 }
40 }
41
42 static void Main(string[] args)
43 {
44 Console.WriteLine("创建三个目录:");
45 Folder folder = new Folder("根目录");
46 Folder folder1 = new Folder("子目录1");
47 Folder folder2 = new Folder("子目录2");
48
49 Console.WriteLine("\r\n创建两个文档:");
50 Word word1 = new Word("word文档1");
51 Word word2 = new Word("word文档2");
52
53 Console.WriteLine("\r\n将子目录1添加到根目录下:");
54 folder.AddChild(folder1);
55 Console.WriteLine("\r\n将子目录2添加到子目录1下:");
56 folder1.AddChild(folder2);
57
58 Console.WriteLine("\r\n将word文档1添加到子目录2下:");
59 folder2.AddChild(word1);
60 Console.WriteLine("\r\n将word文档2添加到根目录下:");
61 folder.AddChild(word2);
62
63 Console.WriteLine("\r\n广度优先列表:");
64 DepthFirstSearch(folder);
65 Console.WriteLine("\r\n深度优先列表:");
66 BreadthFirstSearch(folder);
67
68 Console.ReadKey();
69 }
70
71
72 }
复制代码
注:BreadthFirstSearch为广度优先检索,依次列出所有元素。DepthFirstSearch为深度优先检索,列举完一个文件夹后,返回根目录继续列举其他文件夹。
通过上述实例可以看出,文件夹可以创建N个子文件夹,但文档只能放在文件夹中,无法放在另一个文档中。
安全方式
1 /// <summary>
2 /// 抽象组件类(Component)
3 /// </summary>
4 public abstract class DocumentComponent
5 {
6 public string Name { get; set; }
7 protected List<DocumentComponent> mChildren;
8 public List<DocumentComponent> Children
9 {
10 get { return mChildren; }
11 }
12 public DocumentComponent(string name)
13 {
14 this.Name = name;
15 mChildren = new List<DocumentComponent>();
16 }
17
18 public abstract void Show();
19 }
20
21 /// <summary>
22 /// 节点组件类(Leaf),各类文档,每类型可以添加一个对应类。
23 /// </summary>
24 public sealed class Word : DocumentComponent
25 {
26 public Word(string name)
27 : base(name)
28 { }
29
30 public override void Show()
31 {
32 Console.WriteLine("这是一篇word文档:" + Name);
33 }
34 }
35
36 /// <summary>
37 /// 容器组件类(Composite),文件夹
38 /// </summary>
39 public class Folder : DocumentComponent
40 {
41 public Folder(string name)
42 : base(name)
43 { }
44 public void AddChild(DocumentComponent document)
45 {
46 mChildren.Add(document);
47 Console.WriteLine("文档或文件夹增加成功");
48 }
49 public void RemoveChild(DocumentComponent document)
50 {
51 mChildren.Remove(document);
52 Console.WriteLine("文档或文件夹删除成功");
53 }
54 public override void Show()
55 {
56 Console.WriteLine("这是一个文件夹:" + Name);
57 }
58 }
59
60
61 /// <summary>
62 /// 客户端
63 /// </summary>
64 class Client
65 {
66 /// <summary>
67 /// 广度优先检索
68 /// </summary>
69 /// <param name="component"></param>
70 private static void BreadthFirstSearch(DocumentComponent component)
71 {
72 Queue<DocumentComponent> q = new Queue<DocumentComponent>();
73 q.Enqueue(component);
74 Console.WriteLine(component.Name);
75 while (q.Count > 0)
76 {
77 DocumentComponent temp = q.Dequeue();
78 List<DocumentComponent> children = temp.Children;
79 foreach (DocumentComponent child in children)
80 {
81 Console.WriteLine(child.Name);
82 q.Enqueue(child);
83 }
84 }
85 }
86
87 /// <summary>
88 /// 深度优先检索
89 /// </summary>
90 /// <param name="component"></param>
91 private static void DepthFirstSearch(DocumentComponent component)
92 {
93 Console.WriteLine(component.Name);
94 List<DocumentComponent> children = component.Children;
95 if (children == null || children.Count == 0) return;
96 foreach (DocumentComponent child in children)
97 {
98 DepthFirstSearch(child);
99 }
100 }
101
102 static void Main(string[] args)
103 {
104 Console.WriteLine("创建三个目录:");
105 Folder folder = new Folder("根目录");
106 Folder folder1 = new Folder("子目录1");
107 Folder folder2 = new Folder("子目录2");
108
109 Console.WriteLine("\r\n创建两个文档:");
110 Word word1 = new Word("word文档1");
111 Word word2 = new Word("word文档2");
112
113 Console.WriteLine("\r\n将子目录1添加到根目录下:");
114 folder.AddChild(folder1);
115 Console.WriteLine("\r\n将子目录2添加到子目录1下:");
116 folder1.AddChild(folder2);
117
118 Console.WriteLine("\r\n将word文档1添加到子目录2下:");
119 folder2.AddChild(word1);
120 Console.WriteLine("\r\n将word文档2添加到根目录下:");
121 folder.AddChild(word2);
122
123 Console.WriteLine("\r\n广度优先列表:");
124 DepthFirstSearch(folder);
125 Console.WriteLine("\r\n深度优先列表:");
126 BreadthFirstSearch(folder);
127
128 Console.ReadKey();
129 }
130
131 }
复制代码
从上述实例中可以看出,安全模式其实就是把共有的方法放在抽象类的。
文件夹独有的方法放在容器类中,这样做保证了节点类就没有Add和Remove等无用方法。
四、总结:
组合模式解耦了客户程序与复杂元素内部结构,从而使客户程序可以向处理简单元素一样来处理复杂元素。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
组合
模式
详解
相关帖子
nginx健康检查详解
mysql 主从架构详解
【软件测试】等待的艺术:详解显式等待与隐式等待,告别time.sleep!
多项式入门到进阶超详解
redis持久化详解
redis哨兵集群详解
从零实现富文本编辑器#7-基于组合事件的半受控输入模式
KMP 模式串匹配算法讲解
详解智能体应用——网课生成Agent
设计模式之单例模式
vip免费申请,1年只需15美金$
回复
使用道具
举报
提升卡
置顶卡
沉默卡
喧嚣卡
变色卡
千斤顶
照妖镜
相关推荐
业界
nginx健康检查详解
0
630
鞭氅
2025-08-14
业界
mysql 主从架构详解
0
1046
何书艺
2025-08-15
科技
【软件测试】等待的艺术:详解显式等待与隐式等待,告别time.sleep!
0
585
幌斛者
2025-08-21
安全
多项式入门到进阶超详解
0
554
马璞玉
2025-08-22
业界
redis持久化详解
0
389
石娅凉
2025-08-25
业界
redis哨兵集群详解
0
907
劳欣笑
2025-08-28
业界
从零实现富文本编辑器#7-基于组合事件的半受控输入模式
0
264
后雪闵
2025-09-02
安全
KMP 模式串匹配算法讲解
0
314
赶塑坠
2025-09-02
安全
详解智能体应用——网课生成Agent
0
75
乱蚣
2025-09-03
安全
设计模式之单例模式
0
76
颖顿庐
2025-09-03
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
|
立即注册
回复
本版积分规则
回帖并转播
回帖后跳转到最后一页
浏览过的版块
安全
签约作者
程序园优秀签约作者
发帖
账暴
2025-6-9 08:20:36
关注
0
粉丝关注
21
主题发布
板块介绍填写区域,请于后台编辑
财富榜{圆}
敖可
9984
杭环
9988
凶契帽
9988
4
氛疵
9988
5
黎瑞芝
9988
6
猷咎
9986
7
里豳朝
9986
8
肿圬后
9986
9
蝓俟佐
9984
10
虽裘侪
9984
查看更多