组合模式

# 分类

结构型模式

# 定义

  • 组合模式(Compsite Pattern) 又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次

# 意图

组合模式将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性

# 应用场景

  • 部分与整体的场景。

应用案例

  • 树形菜单
  • window操作系统文件夹
  • 公司组织架构
  • 家族结构
  • 行政区域划分

# 角色与结构图

  • Component:组件类角色 为组合中的对象声明接口,在适当情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理 Component 的子部件。
  • Leaf:叶节点类角色 在组合中表示叶节点对象,叶节点没有子节点。
  • Composite:枝节点类角色 定义有节点行为,用来存储子部件,在 Component 接口中实现与子部件有关的操作,比如增加Add和删除Remove。

下图解释了组合模式中各角色的作用

<<------------------------组合模式结构图--------------------------->>

# 示例代码

透明模式

// 抽象组件类,定义一些抽象方法
public abstract class Component{
  protected string name;

  public Component(string name){
    this.name = name;
  }

  public abstract void AddChild(Component c);
  public abstract void Display(int depth);
}
1
2
3
4
5
6
7
8
9
10
11
// 叶节点
class Leaf : Component{
  public Leaf(string name):base(name){};

  public override void AddChild(Component c){
    Console.WriteLine("Cannot add to a leaf");
  }

  public override void Display(int depth){
    Console.WriteLine(new string("-",depth) + name);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
// 组合节点
class Composite : Component{
  private List<Component> children = new List<Component>();

  public Composite(string name) : base(name){};

  public override void AddChild(Component c){
    children.Add(c);
  }

  public override void Display(int depth){
    Console.WriteLine(new string('-' , depth) + name);
    
    foreach(Component c in children){
      component.Display(depth + 2);
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

安全模式,叶节点不用考虑AddChild这个方法,因为叶节点压根就没有子节点

// 抽象组件类,定义一些抽象方法
public abstract class Component{
  protected string name;

  public Component(string name){
    this.name = name;
  }

  public abstract void Display(int depth);
}
1
2
3
4
5
6
7
8
9
10
// 叶节点
class Leaf : Component{
  public Leaf(string name):base(name){};

  public override void Display(int depth){
    Console.WriteLine(new string("-",depth) + name);
  }
}
1
2
3
4
5
6
7
8
// 组合节点
class Composite : Component{
  private List<Component> children = new List<Component>();

  public Composite(string name) : base(name){};

  public void AddChild(Component c){
    children.Add(c);
  }

  public override void Display(int depth){
    Console.WriteLine(new string('-' , depth) + name);
    
    foreach(Component c in children){
      component.Display(depth + 2);
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 客户端程序
class Client{
  static void main(string[] args){
    Composite root = new Composite("root");
    
    #region 这部分是造数据用----------
    root.Add(new Leaf("Leaf A"));
    root.Add(new Leaf("Leaf B"));

    Composite comp = new Composite("Composite X"); 
    comp.Add(new Leaf("Leaf XA"));
    comp.Add(new Leaf("Leaf XB"));

    root.Add(comp);

    Composite comp2 = new Composite("Composite XY"); 
    comp2.Add(new Leaf("Leaf XYA"));
    comp2.Add(new Leaf("Leaf XYB"));
    comp.Add(comp2);

    root.Add(new Leaf("Leaf XY"));

    Leaf leaf = new Leaf("Leaf D");
    root.Add(leaf);
    root.Remove(leaf);
    #endregion----------

    #region 体现组合模式-主要是递归思想----------
    root.Display(1);
    #endregion----------

    Console.Read();
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
树形结构图
-root
---Leaf A
---Leaf B
---Composite X
-----Leaf XA;
-----Leaf XB;
-----Composite XY
-------Leaf XYA
-------Leaf XYB
---Leaf C
1
2
3
4
5
6
7
8
9
10
11

# 优点

  • 组合模式这样就定义了包含人力资源部和财务部这些基本对象和分公司、办事处等组合对象的类层次结构。基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去,客户代码中,任何用到基本对象的地方都可以使用组合对象了。
  • 这样处理后,用户就不用关心到底是处理一个叶节点还是处理一个组合组件,也就用不着为定义组合而写一些选择判断语句了。
  • 简单点说,组合模式让客户可以一致地使用组合结构和单个对象。
  • 高层模块调用简单。
  • 节点自由哦增加。

# 缺点

  • 在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。

# 小结

  • 主要解决?它在我们树型结构问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
  • 何时使用?1 想表示对象的部分-整体层次结构 2 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
  • 如何解决?树枝和叶子实现统一接口,树枝内部组合该接口。
  • 关键代码?树枝内部组合该接口,并且包含有内部属性列表,里面放组件。
  • 注意:定义时为具体类。

# 大结

对象容器问题。 在面向对象系统中,我们常会遇到一类具有容器特征的对象-即它们在充当对象的同时,又是其他对象的容器。 动机:上述描述的问题根源在于,客户代码过多地依赖于对象容器复杂的内部实现结构,对象容器内部实现结构(而非抽象接口)的变化将引起客户代码的频繁变化,带来了代码的维护性、扩展性等弊端。 如何将客户代码与复杂的对象容器结构解耦?让对象容器自己来实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器? 意图:将对象组合成树形结构以标示“部分-整体”的层次结构。组合使得用户对单个对象和组合对象的使用具有一致性。

# 一句话概括

将对象组合成树形结构以表示“部分-整体”的层次结构。

上次更新: 2025/03/22, 13:47:44
最近更新
01
Git问题集合
01-29
02
安装 Nginx 服务器
01-25
03
安装 Docker 容器
01-25
更多文章>
×
×