十一.结构型设计模式——Composite Pattern(组合模式)

  • 定义

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

  UML类图如下:

  

  其中类和对象的关系如下:

  1. Component(部件抽象接口):为组合的对象申明接口;某些情况下,实现从此接口派生出所有类共有的默认行为;定义一个接口可以访问及管理它的多个子部件(GetChild);如果必要,也可以在递归结构中定义一个接口访问它的父节点,并且实现它。

  2. Leaf(叶部件):在组合中表示叶节点对象,叶节点没有子节点;定义组合中原接口对象的行为。

  3. Composite(组合类):定义有子节点(子部件)的部件行为;存储子节点(子部件);在Component接口中实现与子部件相关的操作。

  4. Client:通过Component接口控制组合部件的对象。

  典型应用的顺序图如下:

  

  上图的NonLeaf1就是组合(Composite)类,它包括多个子组合类,即nonLeaf1ChildX,每个子组合类都有个叶部件类(LeafNode)。客户调用组合类的doIt(),都会引起其子部件的递归调用。

  • 实例1——图形树状对象结构

  当软件中要描述一个图像对象时,它可能包括很多原始节点(叶节点,如线、圆等)及组合节点(线、圆等的集合的复杂组合),其UML类图如下:

  

  

代码
//定义部件抽象类Component
abstract class DrawingElement
{
protected string name;
public DrawingElement(string name)
{
this.name = name;
}
abstract public void Add(DrawingElement d);
abstract public void Remove(DrawingElement d);
abstract public void Display(int indent);
}
//定义叶部件Leaf
class PrimitiveElement : DrawingElement
{
public PrimitiveElement(string name) : base(name) { }
public override void Add(DrawingElement d)
{
Console.WriteLine(
"Cannot Add");
}
public override void Remove(DrawingElement d)
{
Console.WriteLine(
"Cannot Remove");
}
public override void Display(int indent)
{
Console.WriteLine(
new string('-', indent) + " draw a {0}", name);
}
}

//定义组合类,从部件抽象类派生出来,"Composite"
class CompositeElement : DrawingElement
{
private ArrayList elements = new ArrayList();
public CompositeElement(string name) : base(name) { }
public override void Add(DrawingElement d)
{
elements.Add(d);
}
public override void Remove(DrawingElement d)
{
elements.Remove(d);
}
public override void Display(int indent)
{
Console.WriteLine(
new string('-', indent) + "+ " + name);
//显示节点每一个元素
foreach (DrawingElement c in elements)
{
c.Display(indent
+ 2);
}
}
}

//客户应用测试
class Client
{
[STAThread]
static void Main(string[] args)
{
//生成一个树的结构
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);
//增加并且删除叶部件
PrimitiveElement l = new PrimitiveElement("Yellow Line");
root.Add(l);
root.Remove(l);
//递归显示所有节点
root.Display(1);
Console.Read();
}
}
  •  实例2——文档格式化

  继续上面的例子,假设你正在设计一个文档格式化程序,这个程序的作用是把字符(character)格式化为文本行(line of text),很多行就组成栏(column),栏再组成页(page)。一篇文档(document)除了包含很多页,还可能包含其他元素,例如图片之类。栏和页中还可以有框(frame),框中可以再容纳栏。栏、框和行都可以包含图片。从上面的描述,可以得到如下图所示的设计。

  

  这些不同的关联使得系统的复杂度变得巨大。你可以想象,维护这样的一个系统会有多困难,如果使用组合模式,系统的复杂度会大大降低,如下图:

  

代码
//先定义IDocumentElement接口
public interface IDocumentElement
{
void Draw();
}
//定义Image类,实现IDocumentElement接口
public class Image : IDocumentElement
{
int x, y;
public Image(int x, int y)
{
this.x = x;
this.y = y;
}
public void Draw()
{
Console.WriteLine(
"Draw an image at " + x + "," + y);
}
}
//定义Character类,实现IDocumentElement接口
public class Character : IDocumentElement
{
int x, y;
public Character(int x, int y)
{
this.x = x;
this.y = y;
}
public void Draw()
{
Console.WriteLine(
"Draw A Character at " + x + "," + y);
}
}
//定义抽象的CompositeDocumentElement类,实现IDocumentElement接口
public abstract class CompositeDocumentElement : IDocumentElement
{
private ArrayList elements = new ArrayList();
public virtual void Draw()
{
foreach (IDocumentElement idoc in elements)
{
idoc.Draw();
}
}
public void Add(IDocumentElement idoc)
{
elements.Add(idoc);
}
}
//定义Column类,由CompositeDocumentElement派生出来
public class Column : CompositeDocumentElement
{
public override void Draw()
{
Console.WriteLine(
"Column content:");
base.Draw();
}
}
//定义Page类,由CompositeDocumentElement派生出来
public class Page : CompositeDocumentElement
{
public override void Draw()
{
Console.WriteLine(
"Page content:");
base.Draw();
}
}


//客户应用测试
class Client
{
[STAThread]
static void Main(string[] args)
{
Page page
= new Page();
Character c1
= new Character(1, 1);
Image img1
= new Image(2, 2);
Column col
= new Column();
Character c2
= new Character(1, 3);
Image img2
= new Image(2, 3);
col.Add(c2);
col.Add(img2);
page.Add(c1);
page.Add(img1);
page.Add(col);
page.Draw();
Console.Read();
}
}

 

 

  • 优势和缺陷

  组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使得增加新部件也更容易,因为它让客户忽略了层次的不同性,而它的结构又是动态的,提供了对象管理的灵活接口。组合模式对于树结构的控制有着神奇的功效,例如在人力资源系统的组织架构及ERP系统的BOM设计中,组合模式得到重点应用。

  组合模式的缺陷是使得设计变得更加抽象。对象的商业规则如果很复杂,则实现组合模式具有很大挑战性,并且,不是所有的方法都与叶部件子类有关联。

  • 应用情景

  下面的情景很适合应用组合模式:

  1. 你想表示一个对象整体或部分层次。

  2. 你想让客户能够忽略不同对象的层次的变化。

  3. 对象的结构是动态的并且复杂层度不一样,但客户需要一致地处理它们。

posted on 2010-11-03 23:52  tLEE  阅读(804)  评论(0编辑  收藏  举报

导航