一、 合成(Composite)模式

合成模式有时又叫做部分-整体模式(Part-Whole)。合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式可以使客户端将单纯元素与复合元素同等看待。

从和尚的故事谈起

这是小时候我奶奶讲的故事:从前有个山,山里有个庙,庙里有个老和尚在给小和尚讲故事,讲的什么故事呢?从前有个山,山里有个庙……。奶奶的故事要循环多少次,根据你多长时间睡着而定。在故事中有山、有庙、有和尚、有故事。因此,故事的角色有两种:一种里面没有其它角色;另一种内部有其它角色。

对象的树结构

一个树结构由两种节点组成:树枝节点和树叶节点。树枝节点可以有子节点,而一个树叶节点不可以有子节点。除了根节点外,其它节点有且只有一个父节点。

注意:一个树枝节点可以不带任何叶子,但是它因为有带叶子的能力,因此仍然是树枝节点,而不会成为叶节点。一个树叶节点永远不可能带有子节点。

二、 合成模式概述

下图所示的类图省略了各个角色的细节。

可以看出,上面的类图结构涉及到三个角色:

  • 抽象构件(Component)角色:这是一个抽象角色,它给参与组合的对象规定一个接口。这个角色给出共有接口及其默认行为。
  • 树叶构件(Leaf)角色:代表参加组合的树叶对象。一个树叶对象没有下级子对象。
  • 树枝构件(Composite)角色:代表参加组合的有子对象的对象,并给出树枝构件对象的行为。

可以看出,Composite类型的对象可以包含其它Component类型的对象。换而言之,Composite类型对象可以含有其它的树枝(Composite)类型或树叶(Leaf)类型的对象。

合成模式的实现根据所实现接口的区别分为两种形式,分别称为安全模式和透明模式。合成模式可以不提供父对象的管理方法,但合成模式必须在合适的地方提供子对象的管理方法(诸如:add、remove、getChild等)。

透明方式

作为第一种选择,在Component里面声明所有的用来管理子类对象的方法,包括add()、remove(),以及getChild()方法。这样做的好处是所有的构件类都有相同的接口。在客户端看来,树叶类对象与合成类对象的区别起码在接口层次上消失了,客户端可以同等同的对待所有的对象。这就是透明形式的合成模式。

这个选择的缺点是不够安全,因为树叶类对象和合成类对象在本质上是有区别的。树叶类对象不可能有下一个层次的对象,因此add()、remove()以及getChild()方法没有意义,是在编译时期不会出错,而只会在运行时期才会出错。

安全方式

第二种选择是在Composite类里面声明所有的用来管理子类对象的方法。这样的做法是安全的做法,因为树叶类型的对象根本就没有管理子类对象的方法,因此,如果客户端对树叶类对象使用这些方法时,程序会在编译时期出错。

这个选择的缺点是不够透明,因为树叶类和合成类将具有不同的接口。

这两个形式各有优缺点,需要根据软件的具体情况做出取舍决定。

三、 安全式的合成模式的结构

安全式的合成模式要求管理聚集的方法只出现在树枝构件类中,而不出现在树叶构件中。

这种形式涉及到三个角色:

  • 抽象构件(Component)角色:这是一个抽象角色,它给参加组合的对象定义出公共的接口及其默认行为,可以用来管理所有的子对象。在安全式的合成模式里,构件角色并不是定义出管理子对象的方法,这一定义由树枝构件对象给出。
  • 树叶构件(Leaf)角色:树叶对象是没有下级子对象的对象,定义出参加组合的原始对象的行为。
  • 树枝构件(Composite)角色:代表参加组合的有下级子对象的对象。树枝对象给出所有的管理子对象的方法,如add()、remove()、getChild()等。

四、 安全式的合成模式实现

以下示例性代码演示了安全式的合成模式代码:

// Composite pattern -- Structural example  
using System;
using System.Text;
using System.Collections;

// "Component"
abstract class Component
{
  // Fields
  protected string name;

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

  // Operation
  public abstract void Display( int depth );
}

// "Composite"
class Composite : Component
{
  // Fields
  private ArrayList children = new ArrayList();

  // Constructors
  public Composite( string name ) : base( name ) {}

  // Methods
  public void Add( Component component )
  {
    children.Add( component );
  }
  public void Remove( Component component )
  {
    children.Remove( component );
  }
  public override void Display( int depth )
  {
    Console.WriteLine( new String( '-', depth ) + name );

    // Display each of the node's children
    foreach( Component component in children )
      component.Display( depth + 2 );
  }
}

// "Leaf"
class Leaf : Component
{
  // Constructors
  public Leaf( string name ) : base( name ) {}

  // Methods
  public override void Display( int depth )
  {
    Console.WriteLine( new String( '-', depth ) + name );
  }
}

/**//// <summary>
/// Client test
/// </summary>
public class Client
{
  public static void Main( string[] args )
  {
    // Create a tree structure
    Composite root = new Composite( "root" );
    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 );

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

    // Add and remove a leaf
    Leaf l = new Leaf( "Leaf D" );
    root.Add( l );
    root.Remove( l );

    // Recursively display nodes
    root.Display( 1 );
  }
}

五、 透明式的合成模式结构

与安全式的合成模式不同的是,透明式的合成模式要求所有的具体构件类,不论树枝构件还是树叶构件,均符合一个固定的接口。

这种形式涉及到三个角色:

  • 抽象构件(Component)角色:这是一个抽象角色,它给参加组合的对象规定一个接口,规范共有的接口及默认行为。
  • 树叶构件(Leaf)角色:代表参加组合的树叶对象,定义出参加组合的原始对象的行为。树叶类会给出add()、remove()以及getChild()之类的用来管理子类对对象的方法的平庸实现。
  • 树枝构件(Composite)角色:代表参加组合的有子对象的对象,定义出这样的对象的行为。

六、 透明式的合成模式实现

以下示例性代码演示了安全式的合成模式代码:

// Composite pattern -- Structural example  

using System;
using System.Text;
using System.Collections;

// "Component"
abstract class Component
{
  // Fields
  protected string name;

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

  // Methods
  abstract public void Add(Component c);
  abstract public void Remove( Component c );
  abstract public void Display( int depth );
}

// "Composite"
class Composite : Component
{
  // Fields
  private ArrayList children = new ArrayList();

  // Constructors
  public Composite( string name ) : base( name ) {}

  // Methods
  public override void Add( Component component )
  { children.Add( component ); }
  
  public override void Remove( Component component )
  { children.Remove( component ); }
  
  public override void Display( int depth )
  
    Console.WriteLine( new String( '-', depth ) + name );

    // Display each of the node's children
    foreach( Component component in children )
      component.Display( depth + 2 );
  }
}

// "Leaf"
class Leaf : Component
{
  // Constructors
  public Leaf( string name ) : base( name ) {}

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

  public override void Remove( Component c )
  { Console.WriteLine("Cannot remove from a leaf"); }

  public override void Display( int depth )
  { Console.WriteLine( new String( '-', depth ) + name ); }
}

/**//// <summary>
/// Client test
/// </summary>
public class Client
{
  public static void Main( string[] args )
  {
    // Create a tree structure
    Composite root = new Composite( "root" );
    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 );

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

    // Add and remove a leaf
    Leaf l = new Leaf( "Leaf D" );
    root.Add( l );
    root.Remove( l );

    // Recursively display nodes
    root.Display( 1 );
  }
}

七、 使用合成模式时考虑的几个问题

  1. 明显的给出父对象的引用。在子对象里面给出父对象的引用,可以很容易的遍历所有父对象。有了这个引用,可以方便的应用责任链模式。
  2. 在通常的系统里,可以使用享元模式实现构件的共享,但是由于合成模式的对象经常要有对父对象的引用,因此共享不容易实现。
  3. 有时候系统需要遍历一个树枝结构的子构件很多次,这时候可以考虑把遍历子构件的结果暂时存储在父构件里面作为缓存。
  4. 关于使用什么数据类型来存储子对象的问题,在示意性的代码中使用了ArrayList,在实际系统中可以使用其它聚集或数组等。
  5. 客户端尽量不要直接调用树叶类中的方法,而是借助其父类(Component)的多态性完成调用,这样可以增加代码的复用性。

八、 和尚的故事

九、 一个实际应用Composite模式的例子

下面是一个实际应用中的程序,演示了通过一些基本图像元素(直线、园等)以及一些复合图像元素(由基本图像元素组合而成)构建复杂的图形树的过程。

// Composite pattern -- Real World example  

using System;
using System.Collections;

// "Component"
abstract class DrawingElement
{
  // Fields
  protected string name;

  // Constructors
  public DrawingElement( string name )
  { this.name = name; }
 
  // Operation
  abstract public void Display( int indent );
}

// "Leaf"
class PrimitiveElement : DrawingElement
{
  // Constructors
  public PrimitiveElement( string name ) : base( name ) {}

  // Operation
  public override void Display( int indent )
  {
    Console.WriteLine( new String( '-', indent ) + 
      " draw a {0}", name );
  }
}

// "Composite"
class CompositeElement : DrawingElement
{
  // Fields
  private ArrayList elements = new ArrayList();
 
  // Constructors
  public CompositeElement( string name ) : base( name ) {}

  // Methods
  public void Add( DrawingElement d )
  { elements.Add( d ); }

  public void Remove( DrawingElement d )
  { elements.Remove( d ); }

  public override void Display( int indent )
  {
    Console.WriteLine( new String( '-', indent ) +
      "+ " + name );

    // Display each child element on this node
    foreach( DrawingElement c in elements )
      c.Display( indent + 2 );
  }
}
 
/**//// <summary>
///  CompositeApp test
/// </summary>
public class CompositeApp
{
  public static void Main( string[] args )
  {
    // Create a tree structure
    CompositeElement root = new  
      CompositeElement( "Picture" );
    root.Add( new PrimitiveElement( "Red Line" ));
    root.Add( new PrimitiveElement( "Blue Circle" ));
    root.Add( new PrimitiveElement( "Green Box" ));

    CompositeElement comp = new  
      CompositeElement( "Two Circles" );
    comp.Add( new PrimitiveElement( "Black Circle" ) );
    comp.Add( new PrimitiveElement( "White Circle" ) );
    root.Add( comp );

    // Add and remove a PrimitiveElement
    PrimitiveElement l = new PrimitiveElement( "Yellow Line" );
    root.Add( l );
    root.Remove( l );

    // Recursively display nodes
    root.Display( 1 );
  }
}

合成模式与很多其它模式都有联系,将在后续内容中逐步介绍。


参考文献:
阎宏,《Java与模式》,电子工业出版社
[美]James W. Cooper,《C#设计模式》,电子工业出版社
[美]Alan Shalloway  James R. Trott,《Design Patterns Explained》,中国电力出版社
[美]Robert C. Martin,《敏捷软件开发-原则、模式与实践》,清华大学出版社
[美]Don Box, Chris Sells,《.NET本质论 第1卷:公共语言运行库》,中国电力出版社

转载于:https://www.cnblogs.com/365haha/archive/2006/08/28/488435.html

C#设计模式(11)-Composite Pattern相关推荐

  1. 设计模式总结之Composite Pattern(组合模式)

    Composite Pattern(组合模式) 组合模式,将对象组合成树形结构以表示"部分-整体"的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性. 有时候又叫做部 ...

  2. python 设计模式之组合模式Composite Pattern

    #引入一 文件夹对我们来说很熟悉,文件夹里面可以包含文件夹,也可以包含文件. 那么文件夹是个容器,文件夹里面的文件夹也是个容器,文件夹里面的文件是对象. 这是一个树形结构 咱们生活工作中常用的一种结构 ...

  3. 如何让孩子爱上设计模式 ——11.外观模式(Facade Pattern)

    如何让孩子爱上设计模式 --11.外观模式(Facade Pattern) 标签: 设计模式初涉 场景引入 相信各位玩过LOL英雄联盟游戏的童鞋,对下面两个英雄都不会陌生吧:       分别是瑞雯和 ...

  4. PHP设计模式之组合模式(Composite Pattern)

    组合模式是一系列对象组合成树形结构来表示整体和部分之间的关系,组合模式的主要目的是达到,访问组合对象和访问单个对象具有一致性. 这里的组合对象比较特殊,本身可以是由其他的对象组合而成,同事,这个组合对 ...

  5. php组合设计模式(composite pattern)

    过十点. <?php /* The composite pattern is about treating the hierarchy of objects as a single object ...

  6. 深入浅出设计模式——组合模式(Composite Pattern)

    模式动机 对于树形结构,当容器对象(如文件夹)的某一个方法被调用时,将遍历整个树形结构,寻找也包含这个方法的成员对象(可以是容器对象,也可以是叶子对象,如子文件夹和文件)并调用执行.(递归调用) 由于 ...

  7. 设计模式(十一):从文Finder中认识组合模式(Composite Pattern)

    上一篇博客中我们从从电影院中认识了"迭代器模式"(Iterator Pattern),今天我们就从文件系统中来认识一下"组合模式"(Composite Patt ...

  8. 【设计模式】组合模式 Composite Pattern

    树形结构是软件行业很常见的一种结构,几乎随处可见,  比如: HTML 页面中的DOM,产品的分类,通常一些应用或网站的菜单,Windows Form 中的控件继承关系,Android中的View继承 ...

  9. 设计模式(Design Pattern)

    简介 设计模式(Design Pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用.设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案.这些解决方案是众多软件开发人 ...

  10. JAVA设计模式十七--Composite(组合模式)

    组合模式 组合模式(Composite Pattern)有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念   ,客户程序可以向处理简单元素一样来处理复杂元素,从而使 ...

最新文章

  1. Py之scikiti -survival:scikiti -survival库的简介、安装、使用方法之详细攻略
  2. JDK 11 马上就要来了!JDK 12 还会远吗?
  3. centos7中安装JDK8-281版本
  4. SM66 does not have debug button in Q system
  5. 系统架构师5 ***********那就给个合格分了。111
  6. 诗与远方:无题(六十三)- 杂诗,然,矣
  7. BIND9源码分析之 多个view的情况下如何做dynamic update
  8. Dynamips 7200
  9. VideoPlayer参数
  10. Excel怎么把横向的数据变成纵向排列?
  11. 中国氨基酸表面活性剂市场前景展望与发展建议分析报告2022-2028年
  12. 适合小白的SQL Server学习笔记
  13. NP路由器——OE要点整理
  14. 发挥GPU强大动力的CG语言
  15. 实现固定表头和表列的table组件
  16. python数据分析vip班级笔记
  17. cas压力测试之Mac下JMeter的安装和压力测试
  18. QlikView AutoNumber函数
  19. Python 海龟绘图 100 题——第 80 题
  20. 西门子、ABB、霍尼韦尔、中控、力控...中国SCADADCS市场报告

热门文章

  1. php css类,css class是啥
  2. 系统集成Nacos和Feign
  3. java容器输入_Java 容器初识篇
  4. python实验室公众号_区块链研究实验室 | 使用Python编写Tendermint应用程序
  5. 二叉树 --5.1.3 Binary Tree Zigzag Level Order Traversal --图解
  6. 安装虚拟机(二)配置静态ip
  7. matlab平滑曲线_梯度下降法实现路径平滑
  8. 【渝粤教育】国家开放大学2018年秋季 0529-21T高级英语阅读(1) 参考试题
  9. 【渝粤教育】国家开放大学2018年秋季 0365-21T电子商务概论 参考试题
  10. caffe之 Blob、Layer、Net