一、组合模式介绍:
组合模式解耦了客户程序与复杂元素内部结构,从而使客户程序可以向处理简单元素一样来处理复杂元素。允许你将对象组合成树形结构来表现”部分-整体“的层次结构,使得客户以一致的方式处理单个对象以及对象的组合。组合模式实现的最关键的地方是——简单对象和复合对象必须实现相同的接口。
二、设计背景:
在软件开发过程中,我们经常会遇到处理简单对象和复合对象的情况,例如对操作系统中目录的处理就是这样的一个例子,因为目录可以包括单独的文件(例如word、txt、excel),也可以包括文件夹,文件夹又是由文件组成的,由于简单对象和复合对象在功能上区别,导致在操作过程中必须区分简单对象和复合对象,这样就会导致客户调用带来不必要的麻烦,然而作为客户,它们希望能够始终一致地对待简单对象和复合对象,然而组合模式就是解决这样的问题。下面就让我们以这个例子来看看组合模式的实现。
三、相关代码:
1、创建文件抽象类
/// <summary> /// 文件抽象类 /// </summary> public abstract class File { public string Name { get; set; } public File(string name) { this.Name = name; } public abstract void New(); public abstract void AddFile(File file); public abstract void DeleteFile(File file); }
2、创建具体简单对象类(简单对象):Word文档类和Excel文档类
/// <summary> /// 具体简单对象类——Word文档文件 /// </summary> public class Word : File { public Word(string name) : base(name) { } public override void New() { Console.WriteLine("新建Word文档:" + Name); } //因为文件是目录的最小单位,文件下无法创建文件,所以简单对象Word文档文件类里面的AddFolder和RemoveFolder方法没有意义 public override void AddFile(File file) { throw new Exception("不能向Word文件创建文件"); } public override void DeleteFile(File file) { throw new Exception("不能向Word文件删除文件"); } }
/// <summary> /// 具体简单对象类——Excel文档文件 /// </summary> public class Excel : File { public Excel(string name) : base(name) { } public override void New() { Console.WriteLine("新建Excel文档:" + Name); } public override void AddFile(File file) { throw new Exception("不能向Excel文件创建文件"); } public override void DeleteFile(File file) { throw new Exception("不能向Excel文件删除文件"); } }
3、创建文件夹类(复合对象):
/// <summary> /// 文件夹类,由一些文件组成 /// </summary> public class Folder : File { private List<File> file_list = new List<File>(); public Folder(string name) : base(name) { } /// <summary> /// 文件夹创建各种文件 /// </summary> public override void New() { foreach (var item in file_list) { item.New(); } } public override void AddFile(File file) { file_list.Add(file); } public override void DeleteFile(File file) { file_list.Remove(file); } }
4、调用
static void Main(string[] args) { Folder folder = new Folder("一个文件夹和一个Word文档组成的文件夹"); folder.AddFile(new Word("我是Word文档1")); Folder folder2 = new Folder("一个Word文档和一个Excel文档组成的文件夹"); folder2.AddFile(new Word("我是Word文档2")); folder2.AddFile(new Excel("我是Excel文档1")); folder.AddFile(folder2); Excel excel = new Excel("我是Excel文档2"); folder.AddFile(excel); //查看文件夹 folder.New(); Console.ReadKey(); //移除一个文件再查看文件夹 folder.DeleteFile(excel); folder.New(); Console.ReadKey(); }
上面的实现方式称为透明式的组合模式,由于基本文件对象不存在AddFile和DeleteFile方法,上面实现中直接通过抛出一个异常的方式来解决这样的问题的,但是我们想以一种更安全的方式来解决——因为基本文件根本不存在这样的方法,我们是不是可以移除这些方法呢?为了移除这些方法,我们就不得不修改File接口,我们把管理子对象的方法声明放在文件夹对象里面,这样简单对象Word、Excel使用这些方法时在编译时就会出错,这样的一种实现方式我们称为安全式的组合模式。具体代码如下:
/// <summary> /// 文件夹类,由一些文件组成 /// </summary> public class Folder : File { private List<File> file_list = new List<File>(); public Folder(string name) : base(name) { } /// <summary> /// 文件夹创建各种文件 /// </summary> public override void New() { foreach (var item in file_list) { item.New(); } } public void AddFile(File file) { file_list.Add(file); } public void DeleteFile(File file) { file_list.Remove(file); } }
四、组合模式的三个角色:
- 抽象构件(Component)角色:这是一个抽象角色,上面实现中File充当这个角色,在透明式的组合模式里,它给参加组合的对象定义出了公共的接口及默认行为,可以用来管理所有的子对象。在安全式的组合模式里,构件角色并不定义出管理子对象的方法,这一定义由树枝结构对象给出。
- 树叶构件(Leaf)角色:树叶对象时没有下级子对象的对象,上面实现中Word和Excel充当这个角色,定义出参加组合的原始对象的行为
- 树枝构件(Composite)角色:代表参加组合的有下级子对象的对象,上面实现中Folder充当这个角色,树枝对象给出所有管理子对象的方法实现,如AddFile、DeleteFile等。
五、使用场景:
- 需要表示一个对象整体或部分的层次结构。
- 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
六、总结:
优点:
- 组合模式使得客户端代码可以一致地处理对象和对象容器,无需关系处理的单个对象,还是组合的对象容器。
- 将”客户代码与复杂的对象容器结构“解耦。
- 可以更容易地往组合对象中加入新的构件。
缺点:
- 使得设计更加复杂。客户端需要花更多时间理清类之间的层次关系。