设计模式:组合模式(Composite Pattern) 明

作者:TerryLee  创建于:2006-03-11 出处:http://terrylee.cnblogs.com/archive/2006/03/11/347919.html  收录于:2013-02-28

结构图


 

意图


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

适用性


  • 你想表示对象的部分-整体层次结构。
  • 你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。 

实现代码


 这里我们用绘图这个例子来说明Composite模式,通过一些基本图像元素(直线、圆等)以及一些复合图像元素(由基本图像元素组合而成)构建复杂的图形树。在设计中我们对每一个对象都配备一个Draw()方法,在调用时,会显示相关的图形。可以看到,这里复合图像元素它在充当对象的同时,又是那些基本图像元素的一个容器。先看一下基本的类结构图:

 1 using System;
 2 using System.Collections;
 3 public abstract class Graphics
 4 {
 5     protected string _name;
 6     public Graphics(string name)
 7     {
 8         this._name = name;
 9     }
10     public abstract void Draw();
11     public abstract void Add();
12     public abstract void Remove();
13 }
14 public class Picture : Graphics
15 {
16     protected ArrayList picList = new ArrayList();
17     public Picture(string name) : base(name)
18     { }
19     public override void Draw()
20     {
21         Console.WriteLine("Draw a" + _name.ToString());
22         foreach (Graphics g in picList)
23         {
24             g.Draw();
25         }
26     }
27     public override void Add(Graphics g)
28     {
29         picList.Add(g);
30     }
31     public override void Remove(Graphics g)
32     {
33         picList.Remove(g);
34     }
35 }
36 public class Line : Graphics
37 {
38     public Line(string name) : base(name)
39     { }
40     public override void Draw()
41     {
42         Console.WriteLine("Draw a" + _name.ToString());
43     }
44     public override void Add(Graphics g)
45     {
46         //抛出一个我们自定义的异常
47     }
48     public override void Remove(Graphics g)
49     {
50         //抛出一个我们自定义的异常
51     }
52 }
53 public class Circle : Graphics
54 {
55     public Circle(string name): base(name)
56     { }
57     public override void Draw()
58     {
59         Console.WriteLine("Draw a" + _name.ToString());
60     }
61     public override void Add(Graphics g)
62     {
63         //抛出一个我们自定义的异常
64     }
65     public override void Remove(Graphics g)
66     {
67         //抛出一个我们自定义的异常
68     }
69 }
70 public class Rectangle : Graphics
71 {
72     public Rectangle(string name): base(name)
73     { }
74     public override void Draw()
75     {
76         Console.WriteLine("Draw a" + _name.ToString());
77     }
78     public override void Add(Graphics g)
79     {
80         //抛出一个我们自定义的异常
81     }
82     public override void Remove(Graphics g)
83     {
84         //抛出一个我们自定义的异常
85     }
86 }

因为Line,Rectangle,Circle已经没有了子对象,它是一个基本图像元素,因此Add(),Remove()的方法对于它来说没有任何意义,而且把这种错误不会在编译的时候报错,所以需要抛出异常。

这样改进以后,我们可以捕获可能出现的错误,做进一步的处理。上面的这种实现方法属于透明式的Composite模式,如果我们想要更安全的一种做法,就需要把管理子对象的方法声明在树枝构件Picture类里面,这样如果叶子节点Line,Rectangle,Circle使用这些方法时,在编译期就会出错,看一下类结构图:

 1 using System;
 2 using System.Collections;
 3 public abstract class Graphics
 4 {
 5     protected string _name;
 6     public Graphics(string name)
 7     {
 8         this._name = name;
 9     }
10     public abstract void Draw();
11 }
12 public class Picture : Graphics
13 {
14     protected ArrayList picList = new ArrayList();
15     public Picture(string name): base(name)
16     { }
17     public override void Draw()
18     {
19         Console.WriteLine("Draw a" + _name.ToString());
20         foreach (Graphics g in picList)
21         {
22             g.Draw();
23         }
24     }
25     public void Add(Graphics g)
26     {
27         picList.Add(g);
28     }
29     public void Remove(Graphics g)
30     {
31         picList.Remove(g);
32     }
33 }
34 public class Line : Graphics
35 {
36     public Line(string name): base(name)
37     { }
38     public override void Draw()
39     {
40         Console.WriteLine("Draw a" + _name.ToString());
41     }
42 }
43 public class Circle : Graphics
44 {
45     public Circle(string name) : base(name)
46     { }
47     public override void Draw()
48     {
49         Console.WriteLine("Draw a" + _name.ToString());
50     }
51 }
52 public class Rectangle : Graphics
53 {
54     public Rectangle(string name) : base(name)
55     { }
56     public override void Draw()
57     {
58         Console.WriteLine("Draw a" + _name.ToString());
59     }
60 }

这种方式属于安全式的Composite模式,在这种方式下,虽然避免了前面所讨论的错误,但是它也使得叶子节点和树枝构件具有不一样的接口。这种方式和透明式的Composite各有优劣,具体使用哪一个,需要根据问题的实际情况而定。

以下是客户端代码

 1 public class App
 2 {
 3     public static void Main()
 4     {
 5         Picture root = new Picture("Root");
 6         root.Add(new Line("Line"));
 7         root.Add(new Circle("Circle"));
 8         Rectangle r = new Rectangle("Rectangle");
 9         root.Add(r);
10         root.Draw();
11     }
12 }

效果及实现要点


1.Composite模式采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转化“一对一”的关系,使得客户代码可以一致地处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器。

2.将“客户代码与复杂的对象容器结构”解耦是Composite模式的核心思想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的复内部实现结构——发生依赖关系,从而更能“应对变化”。

3.Composite模式中,是将“Add和Remove等和对象容器相关的方法”定义在“表示抽象对象的Component类”中,还是将其定义在“表示对象容器的Composite类”中,是一个关乎“透明性”和“安全性”的两难问题,需要仔细权衡。这里有可能违背面向对象的“单一职责原则”,但是对于这种特殊结构,这又是必须付出的代价。ASP.NET控件的实现在这方面为我们提供了一个很好的示范。

4.Composite模式在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率。

原文地址:https://www.cnblogs.com/Ming8006/p/2937344.html