9.组合模式(Composite Pattern)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了9.组合模式(Composite Pattern)相关的知识,希望对你有一定的参考价值。
动机(Motivate):
组合模式有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
意图(Intent):
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite模式使得用户对单个对象和组合对象的使用具有一致性。
-----------《设计模式》GOF
结构图(Struct):
组合模式有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
意图(Intent):
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite模式使得用户对单个对象和组合对象的使用具有一致性。
-----------《设计模式》GOF
结构图(Struct):
生活中的例子:
适用性:
1.你想表示对象的部分-整体层次结构
2.你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。代码实现:
这里我们用绘图这个例子来说明Composite模式,通过一些基本图像元素(直线、圆等)以及一些复合图像元素(由基本图像元素组合而成)构建复杂的图形树。在设计中我们对每一个对象都配备一个Draw()方法,在调用时,会显示相关的图形。可以看到,这里复合图像元素它在充当对象的同时,又是那些基本图像元素的一个容器。先看一下基本的类结构图:
图中橙色的区域表示的是复合图像元素。
示意性代码:
public abstract class Graphics { protected string _name; public Graphics(string name) { this._name = name; } public abstract void Draw(); }
public class Picture : Graphics { public Picture(string name) : base(name) { } public override void Draw() { } public ArrayList GetChilds() { //返回所有的子对象 return new ArrayList(); } }
public class Line : Graphics { public Line(string name) : base(name) { } public override void Draw() { Console.WriteLine("Draw a " + _name.ToString()); } }
public class Circle : Graphics { public Circle(string name) : base(name) { } public override void Draw() { Console.WriteLine("Draw a " + _name.ToString()); } }
public class Ractangle : Graphics { public Ractangle(string name) : base(name) { } public override void draw() { Console.WriteLine("Draw a " + _name.ToString()); } }
示意代码:
public abstract class Graphics { protected string _name; public Graphics(string name) { this._name = name; } public abstract void Draw(); public abstract void Add(); public abstract void Remove(); }
public class Picture : Graphics { protected ArrayList picList = new ArrayList(); public Picture(string name) : base(name) { } public override void Draw() { Console.WriteLine("Draw a" + _name.ToString()); foreach (Graphics g in picList) { g.Draw(); } } public ArrayList GetChilds() { //返回所有的子对象 return new ArrayList(); } public override void Add(Graphics g) { picList.Add(g); } public override void Remove(Graphics g) { picList.Remove(g); } }
public class Line : Graphics { public Line(string name): base(name){ } public override void Draw() { Console.WriteLine("Draw a " + _name.ToString()); } public override void Add() { } public override void Remove() { } }
public class Circle : Graphics { public Circle(string name) : base(name) { } public override void Draw() { Console.WriteLine("Draw a " + _name.ToString()); } public override void Add() { } public override void Remove() { } }
public class Ractangle : Graphics { public Ractangle(string name) : base(name) { } public override void Draw() { Console.WriteLine("Draw a " + _name.ToString()); } public override void Add() { } public override void Remove() { } }
1 public class Line : Graphics
2 {
3 public Line(string name)
4 : base(name)
5 { }
6
7 public override void Draw()
8 {
9 Console.WriteLine("Draw a" + _name.ToString());
10 }
11 public override void Add(Graphics g)
12 {
13 //抛出一个我们自定义的异常
14 }
15 public override void Remove(Graphics g)
16 {
17 //抛出一个我们自定义的异常
18 }
19 }
这样改进以后,我们可以捕获可能出现的错误,做进一步的处理。上面的这种实现方法属于透明式的Composite模式,如果我们想要更安全的一种做法,就需要把管理子对象的方法声明在树枝构件Picture类里面,这样如果叶子节点Line,Rectangle,Circle使用这些方法时,在编译期就会出错,看一下类结构图:2 {
3 public Line(string name)
4 : base(name)
5 { }
6
7 public override void Draw()
8 {
9 Console.WriteLine("Draw a" + _name.ToString());
10 }
11 public override void Add(Graphics g)
12 {
13 //抛出一个我们自定义的异常
14 }
15 public override void Remove(Graphics g)
16 {
17 //抛出一个我们自定义的异常
18 }
19 }
示意代码:
1 public abstract class Graphics
2 {
3 protected string _name;
4
5 public Graphics(string name)
6 {
7 this._name = name;
8 }
9 public abstract void Draw();
10 }
11
12 public class Picture : Graphics
13 {
14 protected ArrayList picList = new ArrayList();
15
16 public Picture(string name)
17 : base(name)
18 { }
19 public override void Draw()
20 {
21 Console.WriteLine("Draw a" + _name.ToString());
22
23 foreach (Graphics g in picList)
24 {
25 g.Draw();
26 }
27 }
28
29 public void Add(Graphics g)
30 {
31 picList.Add(g);
32 }
33 public void Remove(Graphics g)
34 {
35 picList.Remove(g);
36 }
37 }
38
39 public class Line : Graphics
40 {
41 public Line(string name)
42 : base(name)
43 { }
44
45 public override void Draw()
46 {
47 Console.WriteLine("Draw a" + _name.ToString());
48 }
49 }
50
51 public class Circle : Graphics
52 {
53 public Circle(string name)
54 : base(name)
55 { }
56
57 public override void Draw()
58 {
59 Console.WriteLine("Draw a" + _name.ToString());
60 }
61 }
62
63 public class Rectangle : Graphics
64 {
65 public Rectangle(string name)
66 : base(name)
67 { }
68
69 public override void Draw()
70 {
71 Console.WriteLine("Draw a" + _name.ToString());
72 }
73 }
这种方式属于安全式的Composite模式,在这种方式下,虽然避免了前面所讨论的错误,但是它也使得叶子节点和树枝构件具有不一样的接口。这种方式和透明式的Composite各有优劣,具体使用哪一个,需要根据问题的实际情况而定。通过Composite模式,客户程序在调用Draw()的时候不用再去判断复杂图像元素中的子对象到底是基本图像元素,还是复杂图像元素,看一下简单的客户端调用:2 {
3 protected string _name;
4
5 public Graphics(string name)
6 {
7 this._name = name;
8 }
9 public abstract void Draw();
10 }
11
12 public class Picture : Graphics
13 {
14 protected ArrayList picList = new ArrayList();
15
16 public Picture(string name)
17 : base(name)
18 { }
19 public override void Draw()
20 {
21 Console.WriteLine("Draw a" + _name.ToString());
22
23 foreach (Graphics g in picList)
24 {
25 g.Draw();
26 }
27 }
28
29 public void Add(Graphics g)
30 {
31 picList.Add(g);
32 }
33 public void Remove(Graphics g)
34 {
35 picList.Remove(g);
36 }
37 }
38
39 public class Line : Graphics
40 {
41 public Line(string name)
42 : base(name)
43 { }
44
45 public override void Draw()
46 {
47 Console.WriteLine("Draw a" + _name.ToString());
48 }
49 }
50
51 public class Circle : Graphics
52 {
53 public Circle(string name)
54 : base(name)
55 { }
56
57 public override void Draw()
58 {
59 Console.WriteLine("Draw a" + _name.ToString());
60 }
61 }
62
63 public class Rectangle : Graphics
64 {
65 public Rectangle(string name)
66 : base(name)
67 { }
68
69 public override void Draw()
70 {
71 Console.WriteLine("Draw a" + _name.ToString());
72 }
73 }
1 public class App
2 {
3 public static void Main()
4 {
5 Picture root = new Picture("Root");
6
7 root.Add(new Line("Line"));
8 root.Add(new Circle("Circle"));
9
10 Rectangle r = new Rectangle("Rectangle");
11 root.Add(r);
12
13 root.Draw();
2 {
3 public static void Main()
4 {
5 Picture root = new Picture("Root");
6
7 root.Add(new Line("Line"));
8 root.Add(new Circle("Circle"));
9
10 Rectangle r = new Rectangle("Rectangle");
11 root.Add(r);
12
13 root.Draw();
Composite模式实现要点:
1.Composite模式采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转化“一对一”的关系,使得客户代码可以一致地处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器。
2.将“客户代码与复杂的对象容器结构”解耦是Composite模式的核心思想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的复内部实现结构——发生依赖关系,从而更能“应对变化”。
3.Composite模式中,是将“Add和Remove等和对象容器相关的方法”定义在“表示抽象对象的Component类”中,还是将其定义在“表示对象容器的Composite类”中,是一个关乎“透明性”和“安全性”的两难问题,需要仔细权衡。这里有可能违背面向对象的“单一职责原则”,但是对于这种特殊结构,这又是必须付出的代价。ASP.NET控件的实现在这方面为我们提供了一个很好的示范。
4.Composite模式在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率。以上是关于9.组合模式(Composite Pattern)的主要内容,如果未能解决你的问题,请参考以下文章
设计模式之八:组合模式(Composite Pattern)
设计模式 -- 组合模式 (Composite Pattern)
尚硅谷设计模式学习(10)---[组合模式(Composite Pattern)]