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

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

结构图


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

意图


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

适用性


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

实现代码


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

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

 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使用这些方法时,在编译期就会出错,看一下类结构图:

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

 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模式在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率。

 

你可能感兴趣的:(Pattern)