【设计模式】组合模式 Composite Pattern
树形结构是软件行业很常见的一种结构,几乎随处可见, 比如: HTML 页面中的DOM,产品的分类,通常一些应用或网站的菜单,Windows Form 中的控件继承关系,Android中的View继承关系,部门的组织架构,Windows 资源管理器 等等都是树形结构。

Windows 资源管理
树形结构是很有特点的一种数据结构, 下图是一棵树:

树结构有几个术语:
根节点:最高的节点被称为根节点,上图中的红色节点是根节点。根节点没有父节点。
父节点:如果一个节点的下面链接着其它节点那上层节点被称作该节点的父节点,下层节点被称作父节点的子节点。除了根节点外子节点有且只有一个父节点。上图中的红色和黄色都是父节点。
叶子节点:每一个节点有0个或多个子节点。有0个(没有)子节点的节点称作叶子节点。上图中的绿色节点都是叶子节点。
如果要给树形结构上增加或者删除一些节点该如何处理呢? 组合模式提供了面向对象优雅的处理这种数据结构的方法。
一、组合模式的定义
组合模式(Composite Pattern):组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,组合模式又可以称为“整体—部分”(Part-Whole)模式,它是一种对象结构型模式。
二、组合模式结构图

组合模式结构图
1、Component(抽象构件):
它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。
2 、Composite(容器构件):
它在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。
3、Leaf(叶子构件):
它在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。
三、组合模式的示例代码
public abstract class Component
{
public abstract void Operaton(int depth);
public abstract void Add(Component component);
public abstract void Remove(Component component);
public abstract Component GetChild(int i);
} public class Composite : Component
{
private IList<Component> components = new List<Component>();
public override void Operaton(int depth)
{
Console.WriteLine(new String(' ', depth - 2) + "|");
Console.WriteLine(new String(' ', depth - 2) + "--" + this.GetType().Name + " Opration");
foreach (var component in components)
{
component.Operaton(depth + 2);
}
} public override void Add(Component component)
{
components.Add(component);
} public override void Remove(Component component)
{
components.Remove(component);
} public override Component GetChild(int i)
{
return components[i];
}
}
public class Leaf : Component
{
public override void Operaton(int depth)
{
Console.WriteLine(new String(' ', depth - 2) + "|");
Console.WriteLine(new String(' ', depth - 2) + "--" + this.GetType().Name + " Opration");
} public override void Add(Component component)
{
Console.WriteLine("Cannot add to a leaf");
} public override void Remove(Component component)
{
Console.WriteLine("Cannot remove from a leaf");
} public override Component GetChild(int i)
{
throw new Exception("Cannot get a child from a leaf"); }
}
客户端调用:
static void Main(string[] args)
{
Component root = new Composite.Structure.Composite();
root.Add(new Leaf());
root.Add(new Leaf()); var composite = new Composite.Structure.Composite();
composite.Add(new Leaf());
composite.Add(new Leaf()); root.Add(composite); var componsite2 = new Composite.Structure.Composite();
composite.Add(componsite2); componsite2.Add(new Leaf());
componsite2.Add(new Leaf()); root.Operaton(2);
Console.ReadKey();
}
输出结果

四、组合模式实例
现在有一个新闻系统,这个新闻系统里有若干类别,每一个子类里又包含若干分类,理论上是一个无限级的树形结构,同时每个分类上都可能包含具体的新闻。结构如下:
新闻系统分类组织机构图
上图中的白色部分表示分类(Category),绿色部分表示新闻(News), 现在要添加新闻的分类,删除新闻,以及将新闻展示出来。下面我们用组合模式来实现这些功能.
通过分析,可以提出构件 NewsComponent, News 和Category 三个对象,NewsComponent相当于组合模式的抽象构建,Category相当于 容器构建,News相当于叶子构建。这样我们就可以画出新闻系统核心UML图:

新闻系统代码:
public abstract class NewsComponent
{
protected string title;
public NewsComponent(string title)
{
this.title = title;
}
public abstract void Add(NewsComponent newsComponent);
public abstract void Remove(NewsComponent newsComponent);
public abstract void Display(int depath);
} public class Category : NewsComponent
{
private IList<NewsComponent> categories = new List<NewsComponent>();
public Category(string title):base(title)
{
}
public override void Add(NewsComponent newsComponent)
{
categories.Add(newsComponent);
} public override void Remove(NewsComponent newsComponent)
{
categories.Add(newsComponent);
} public override void Display(int depath)
{
Console.WriteLine(new String('-', depath) + title);
foreach (var category in categories)
{
category.Display(depath+2);
}
}
}
public class News: NewsComponent
{
private string content;
public News(string title, string content):base(title)
{
this.content = content;
}
public override void Add(NewsComponent newsComponent)
{
Console.WriteLine("Cannot add to a News");
} public override void Remove(NewsComponent newsComponent)
{
Console.WriteLine("Cannot remove from a News");
} public override void Display(int depath)
{
Console.WriteLine(new String('-', depath) + this.title + "[content]:" + this.content);
}
}
客户端调用:
static void Main(string[] args)
{
NewsComponent newsComponent = new Category("新闻");
newsComponent.Add(new Category("政治新闻"));
newsComponent.Add(new News("【政治头条】-美国大选 特朗普生出将于将在北京时间明日凌晨3:45宣誓就职美国总统", "..."));
newsComponent.Add(new Category("政治风云")); var a = new Category("娱乐新闻");
a.Add(new News("【娱乐头条】-由王安全执导的<<监狱风云>>将于今天0点在各大影院全面上映", "...."));
a.Add(new Category("娱乐八卦"));
newsComponent.Add(a); var b = new Category("财经新闻");
b.Add(new News("【财经头条】-由于受土耳其货币危机的影响,美国及欧洲股市全线跌幅超1%", "...")); var c = new Category("股市风云");
b.Add(c); c.Add(new Category("欧洲股市"));
c.Add(new Category("美股风云"));
c.Add(new Category("日韩股市"));
newsComponent.Add(b); var d = new Category("体育新闻");
var e = new Category("篮球");
d.Add(e);
var f=new Category("NBA");
var g= new Category("CBA");
e.Add(g);
e.Add(f); newsComponent.Add(d); newsComponent.Display(1);
Console.ReadKey();
}
输出结果:

五、透明组合模式与安全组合模式
在上面的实例中我们发现,Component构件类中出现了Add,Remove方法,但是在叶子节点类(Leaf)中不得不去实现,但是叶子节点是不需要这些方法的,看起来有些鸡肋。虽然客户端实现了无差别调用,虽然可以针对抽象编程,但是一旦调用到了叶子节点的这些方法,软件可能会出现异常或者无意义的调用。那么我们有什么方法来改变呢?
可以将这些方法从Component中移到Composite 中,这样就可以避免这种情况。只在Component中定义Composite和Leaf公用的方法。
那么组合模式的结构图就变成这样:
这时图中的各个角色没有变化,仅仅是在Component去掉了一些抽象方法,在调用的时就不能用抽象构建Component来声明了,要用具体的Composite来声明,这样在客户端调用时就需要区别对待Composite和Leaf了,不能针对抽象Component构件编程了。
1、Component2(抽象构件):
它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。
2 、Composite2(容器构件):
它在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。
3、Leaf2(叶子构件):
它在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。
在组合模式中根据抽象构建定义的形式, 可以将组合模式更称 安全组合模式和透明组合模式。
安全组合模式:
安全组合模式只在抽象构件类中定义叶子节点(Leaf)和容器节点(Composite)都共有的方法,将容器方法移至容器节点中, 它的一般结构如下图:
安全组合模式UML图
安全模式的好处是,在客户端调用叶子节点时不会出现调用不安全的方法,因为叶子节点没有该方法,抽象构件中也没有该方法。不好的地方是,叶子节点和容器节点的实现出现了差别,不能在客户端统一使用抽象构件(Component)来编程了,必须要区别对待叶子节点(Leaf)和容器节点(Component)。
透明组合模式
透明组合模式是将对容器构件的管理方法都定义在抽象构件(Component)中,在叶子节点(Leaf)和容器节点(Composite)都进行实现,在叶子节点中针对相关的管理方法进行相关异常处理,或者友好提示的处理实现, 一般情况下透明组合模式的UML如下:

透明模式UML图
透明模式的好处是可以给客户端调用时提供统一,无差别的对待叶子节点(Leaf)和容器节点(Composite),并且可以针对抽象构件(Component)进行编程。透明模式的缺点是如果调用到叶子节点(Leaf) 上的相关方法会导致程序异常。
六、组合模式的优点
- 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
- 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
- 在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
- 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。
七、组合模式的缺点
组合模式,控制容器节点的类型不太容易。
八、组合模式的使用场景
- 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们。
- 在一个使用面向对象语言开发的系统中需要处理一个树形结构。
- 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型。
Composite模式在平常的开发过程中使用的非常多,因为他提供了一种面向对象的操作树形结构的方法,树形结构在开发中频繁出现。
【设计模式】组合模式 Composite Pattern的更多相关文章
- 浅谈设计模式--组合模式(Composite Pattern)
组合模式(Composite Pattern) 组合模式,有时候又叫部分-整体结构(part-whole hierarchy),使得用户对单个对象和对一组对象的使用具有一致性.简单来说,就是可以像使用 ...
- 设计模式 - 组合模式(composite pattern) 迭代器(iterator) 具体解释
组合模式(composite pattern) 迭代器(iterator) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy 參考组合模式(composit ...
- 设计模式 -- 组合模式 (Composite Pattern)
定义: 对象组合成部分整体结构,单个对象和组合对象具有一致性. 看了下大概结构就是集团总公司和子公司那种层级结构. 角色介绍: Component :抽象根节点:其实相当去总公司,抽象子类共有的方法: ...
- C#设计模式——组合模式(Composite Pattern)
一.概述 在软件开发中,我们往往会遇上类似树形结构的对象体系.即某一对象既可能在树形结构中作为叶节点存在,也可能作为分支节点存在.比如在文件系统中,文件是作为叶节点存在,而文件夹就是分支节点.在设计这 ...
- 乐在其中设计模式(C#) - 组合模式(Composite Pattern)
原文:乐在其中设计模式(C#) - 组合模式(Composite Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 组合模式(Composite Pattern) 作者:weba ...
- 设计模式系列之组合模式(Composite Pattern)——树形结构的处理
说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...
- 二十四种设计模式:组合模式(Composite Pattern)
组合模式(Composite Pattern) 介绍将对象组合成树形结构以表示"部分-整体"的层次结构.它使得客户对单个对象和复合对象的使用具有一致性.示例有一个Message实体 ...
- python 设计模式之组合模式Composite Pattern
#引入一 文件夹对我们来说很熟悉,文件夹里面可以包含文件夹,也可以包含文件. 那么文件夹是个容器,文件夹里面的文件夹也是个容器,文件夹里面的文件是对象. 这是一个树形结构 咱们生活工作中常用的一种结构 ...
- 设计模式-12组合模式(Composite Pattern)
1.模式动机 很多时候会存在"部分-整体"的关系,例如:大学中的部门与学院.总公司中的部门与分公司.学习用品中的书与书包.在软件开发中也是这样,例如,文件系统中的文件与文件夹.窗体 ...
随机推荐
- 初探机器学习之使用讯飞TTS服务实现在线语音合成
最近在调研使用各个云平台提供的AI服务,有个语音合成的需求因此就使用了一下科大讯飞的TTS服务,也用.NET Core写了一个小示例,下面就是这个小示例及其相关背景知识的介绍. 一.什么是语音合成(T ...
- java~springcloud微服务目录索引
回到占占推荐博客索引 最近写了不过关于java,spring,微服务的相关文章,今天把它整理一下,方便大家学习与参考. java~springcloud微服务~目录索引 springcloud~服务注 ...
- MyISAM加锁分析
为什么加锁 你正在读着你喜欢的女孩递给你的信,看到一半的时候,她的好闺蜜过来瞄了一眼(假设她会隐身术,你看不到她),她想把"我很喜欢你"改成"我不喜欢你",刚把 ...
- python微信聊天机器人改进版,定时或触发抓取天气预报、励志语录等,向好友推送
最近想着做一个微信机器人,主要想要实现能够每天定时推送天气预报或励志语录,励志语录要每天有自动更新,定时或当有好友回复时,能够随机推送不同的内容.于是开始了分析思路.博主是采用了多线程群发,因为微信对 ...
- Dubbo Mesh 在闲鱼生产环境中的落地实践
本文作者至简曾在 2018 QCon 上海站以<Service Mesh 的本质.价值和应用探索>为题做了一次分享,其中谈到了 Dubbo Mesh 的整体发展思路是“借力开源.反哺开源” ...
- Docker入门(三)使用Docker Compose
Compose介绍 Compose 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排.Compose 是一个用户定义和运行多个容器的 Docker 应用程序.在 ...
- docker常规操作——启动、停止、重启容器实例
一.启动一个已经停止的容器实例 docker start 容器ID或容器名,建议使用容器ID,容器ID支持模糊查询而容器名称不支持1. 先查看已经暂停的容器实例信息 2. 通过docker start ...
- 设计模式 | 观察者模式/发布-订阅模式(observer/publish-subscribe)
定义: 定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己. 结构:(书中图,侵删) 一个抽象的观察者接口, ...
- Android For OpenCV的环境搭建
OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux.Windows.Android和Mac OS操作系统上.它轻量级而且高效--由一系列 C 函数和少量 C++ 类 ...
- SQL学习(1)初学实验:SQL Server基本配置及基本操作
网络配置.远程连接配置: 防火墙设置: SQL Server的默认端口号是1433. 网络配置: SQLServer Configuration Manager中的客户端协议,众多IP中随便选一个,比 ...