结构型模式 - 组合模式Composite Pattern
学习而来,代码是自己敲的。也有些自己的理解在里边,有问题希望大家指出。

组合模式的定义与特点
组合(Composite Pattern)模式的定义:有时又叫作整体-部分(Part-Whole)模式,它是一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,使用户对单个对象和组合对象具有一致的访问性,属于结构型设计模式。
组合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中,顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点,树形结构图如下。
由上图可以看出,其实根节点和树枝节点本质上属于同一种数据类型,可以作为容器使用;而叶子节点与树枝节点在语义上不属于用一种类型。但是在组合模式中,会把树枝节点和叶子节点看作属于同一种数据类型(用统一接口定义),让它们具备一致行为。
这样,在组合模式中,整个树形结构中的对象都属于同一种类型,带来的好处就是用户不需要辨别是树枝节点还是叶子节点,可以直接进行操作,给用户的使用带来极大的便利。
组合模式的主要优点有:
- 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
- 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;
其主要缺点是:
- 设计较复杂,客户端需要花更多时间理清类之间的层次关系;
- 不容易限制容器中的构件;
- 不容易用继承的方法来增加构件的新功能;
组合模式的结构与实现
组合模式的结构不是很复杂,下面对它的结构和实现进行分析。
1. 模式的结构
组合模式包含以下主要角色。
- 抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。(总的抽象类或接口,定义一些通用的方法,比如新增、删除)
- 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件。
- 树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。
组合模式分为透明式的组合模式和安全式的组合模式。
(1) 透明方式
在该方式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。但其缺点是:树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。其结构图如图 1 所示。
(2) 安全方式
在该方式中,将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了上一种方式的安全性问题,但由于叶子和分支有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。其结构图如图 2 所示。
2. 模式的实现
假如要访问集合 c0={leaf1,{leaf2,leaf3}} 中的元素,其对应的树状图如图 3 所示。

案例

代码样例
using DesignPattern.CompositePattern;
using System;
namespace DesignPattern
{
internal class Program
{
static void Main(string[] args)
{
CompositeHelper();
}
#region Pattern - Composite
static void CompositeHelper()
{
Composite _root = new TreeLimb("小三门目录");
Composite art = new TreeLimb("美术");
Composite sports = new TreeLimb("体育");
Composite music = new TreeLimb("音乐");
_root.Add(art);
_root.Add(sports);
_root.Add(music);
Composite painting = new TreeLimb("油画类");
art.Add(painting);
Composite running = new TreeLimb("跑步类");
Composite swimming = new TreeLimb("游泳类");
sports.Add(running);
sports.Add(swimming);
Composite soprano = new TreeLimb("高音类");
Composite opera = new TreeLimb("戏曲类");
Composite bass = new TreeLimb("低音类");
music.Add(soprano);
music.Add(opera);
music.Add(bass);
Composite painting1 = new Leaf("油画精选1");
Composite painting2 = new Leaf("油画精选2");
painting.Add(painting1);
painting.Add(painting2);
Composite opera1 = new TreeLimb("戏曲选集1");
Composite opera2 = new TreeLimb("戏曲选集2");
Composite opera3 = new TreeLimb("戏曲选集3");
opera.Add(opera1);
opera.Add(opera2);
opera.Add(opera3);
opera.Remove(opera2);
Composite swimming1 = new Leaf("花样游泳");
Composite swimming2 = new Leaf("200m游泳");
Composite swimming3 = new Leaf("800m接力游泳");
Composite swimming4 = new Leaf("1600m接力游泳");
swimming.Add(swimming1);
swimming.Add(swimming2);
swimming.Add(swimming3);
swimming.Add(swimming4);
_root.Depth(0);
Console.WriteLine();
Console.WriteLine("文章数:" + _root.ShowCount());
Console.ReadLine();
}
#endregion
}
}
//======================================================================================
using System;
using System.Collections.Generic;
namespace DesignPattern.CompositePattern
{
/// <summary>
/// 抽象合成类
/// </summary>
public abstract class Composite
{
/// <summary>
/// 组件名
/// </summary>
public string Name;
public Composite(string name)
{
Name = name;
}
/// <summary>
/// 增加子节点
/// </summary>
public abstract void Add(Composite composite);
/// <summary>
/// 删除子节点
/// </summary>
public abstract void Remove(Composite composite);
/// <summary>
/// 返回最终子节点数量
/// </summary>
public abstract int ShowCount();
/// <summary>
/// 节点展示深度
/// </summary>
/// <param name="depth"> - 的数量</param>
public abstract void Depth(int depth);
}
/// <summary>
/// 枝干节点
/// </summary>
public class TreeLimb : Composite
{
private List<Composite> m_Composite = new List<Composite>();
public TreeLimb(string name) : base(name)
{
Name = name;
}
public override void Add(Composite component)
{
m_Composite.Add(component);
}
public override void Depth(int depth)
{
Console.WriteLine(new string('-', depth * 2) + Name);
foreach (Composite item in m_Composite)
{
item.Depth(depth + 1);
}
}
public override void Remove(Composite component)
{
m_Composite.Remove(component);
}
public override int ShowCount()
{
int count = 0;
foreach (var item in m_Composite)
{
count += item.ShowCount();
}
return count;
}
}
/// <summary>
/// 子节点
/// </summary>
public class Leaf : Composite
{
public Leaf(string name) : base(name)
{
Name = name;
}
public override void Add(Composite composite)
{
throw new InvalidOperationException("叶子节点不能添加元素");
}
public override void Depth(int depth)
{
Console.WriteLine(new string('-', depth * 2) + Name);
}
public override void Remove(Composite composite)
{
throw new InvalidOperationException("叶子节点不能删除元素");
}
public override int ShowCount()
{
return 1;
}
}
}
运行结果:

希望大家:点赞,留言,关注咯~
唠家常
今日分享结束啦,小伙伴们你们get到了么,你们有没有更好的办法呢,可以评论区留言分享,也可以加我QQ:841298494,大家一起进步。
- 客官,看完get之后记得点赞哟!
- 小伙伴你还想要别的知识?好的呀,分享给你们
今日推荐
结构型模式 - 组合模式Composite Pattern的更多相关文章
- 【设计模式】结构型05组合模式(Composite Pattern)
组合模式(Composite Pattern) 意图:将对象组合成树形结构以表示"部分-整体"的层次结构.组合模式使得用户对单个对象和组合对象的使用具有一致性. 主要解决:它在我们 ...
- 【设计模式】结构型03外观模式(Facade Pattern)
[设计模式]结构型02装饰模式(Decorator Pattern) 意图:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 主要解决:降低访问 ...
- 【结构型】Flyweight模式
享元模式的主要目的.意图是为对象的大量使用提供一种共享机制.该模式的思想重在复用.共享复用.像文字.列表中的格子等这类多数都是需要考虑复用技术,否则必将大量耗费内存空间而使资源以及性能等大量耗费.该模 ...
- python设计模式---结构型之代理模式
主要想着nginx:) from abc import ABCMeta, abstractmethod # 结构型设计模式---代理模式 class Actor: def __init__(self) ...
- 使用C# (.NET Core) 实现组合设计模式 (Composite Pattern)
本文的概念性内容来自深入浅出设计模式一书. 本文需结合上一篇文章(使用C# (.NET Core) 实现迭代器设计模式)一起看. 上一篇文章我们研究了多个菜单一起使用的问题. 需求变更 就当我们感觉我 ...
- 【设计模式】结构型04桥接模式(Bridge Pattern)
学习地址:http://www.runoob.com/design-pattern/bridge-pattern.html 桥接模式(Bridge Pattern) 桥接模式(Bridge patte ...
- java设计模式--结构型模式--组合模式
什么是组合模式,这个有待研究,个人觉得是各类组合而形成的一种结构吧. 组合模式: 组合模式 概述 将对象组合成树形结构以表示"部分-整体"的层次结构."Composite ...
- JAVA设计模式 5【结构型】代理模式的理解与使用
今天要开始我们结构型 设计模式的学习,设计模式源于生活,还是希望能通过生活中的一些小栗子去理解学习它,而不是为了学习而学习这些东西. 结构型设计模式 结构型设计模式又分为 类 结构型 对象 结构型 前 ...
- Composite模式 组合模式
Android的ViewGroup 和 View 的关系,即是采用组合模式 1. 概述 在数据结构里面,树结构是很重要,我们可以把树的结构应用到设计模式里面. 例子1:就是多级树形菜单. 例子2:文件 ...
随机推荐
- IPython的使用技巧
?打印IPython简介 在IPython中直接输入?,可以打印出IPython的功能介绍 object ?内省功能 在变量后面加上?,可以打印出该变量的详细信息.例如图中一个列表对象,打印出该对象的 ...
- Agda学习笔记1
目录 Agda学习笔记1 快捷键 refl Natural Number 自然数集合 operations rewrite cong 加法结合律 加法交换律 乘法分配律 比较大小 衍生的一些证明 be ...
- jmeter接口性能测试【CSV文件读取+接口关联+设置集合点】
一.前言 周计划上安排了个接口性能测试的任务,便开始了职业生涯的第一个接口性能测试... 接口进行压测之前,首先需要调通脚本.有两种方式,一种是通过抓包工具(如fiddler)抓取业务接口:另一种是通 ...
- Window使用PowerShell改文件时间戳
We cross infinity with every step; we meet eternity in every second. 我们每一步都跨过无穷,每一秒都遇见永恒. Window使用Po ...
- mysql 多个结构不同表查询 返回相同字段名
( select ID,数据库原字段名1 AS 统一字段名1,数据库原字段名2 AS 统一字段名2 from 第一个表名 WHERE 1) UNION(联合表查询)( select ID,数据库原字段 ...
- Training: WWW-Robots
原题链接:http://www.wechall.net/challenge/training/www/robots/index.php 打开网页他给我们说什么 说什么这是一个小挑战,你将会了解到机器人 ...
- c++题目:数迷
c++题目:数迷 题目 [题目描述] 给出含有N×N个格子的正方形表格,要求每个格子都填上一个个位数(范围1-N),使得每行.每列以及同一斜线上的数字都不同.部分格子已经填好数字.求满足题意的方案数. ...
- Springboot2.6集成Email
Springboot集成Email 老版本 这时候的JavaMailSender是受到Spring的托管的,我们只需要配置参数就行了 !如何判断是否是被Springboot托管的:以下代码IDEA会自 ...
- 10 STL-list
重新系统学习c++语言,并将学习过程中的知识在这里抄录.总结.沉淀.同时希望对刷到的朋友有所帮助,一起加油哦! 生命就像一朵花,要拼尽全力绽放!死磕自个儿,身心愉悦! 写在前面,本篇章主要介绍S ...
- JavaEE Day11 BootStrap
之前:前端知识 HTML+CSS+JavaScript 不好写 今日内容:前端的开发框架,内部定义了丰富的CSS样式和JS代码,只需要拿来用就行 会用即可 一.BootStrap ...