设计模式:组合模式(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/archive/2013/02/28/2937344.html