Composite
组合模式:也成为整体-部分模式 ,通过将单个对象(叶子节点)和组合对象(树枝节点)用相同的接口进行表示,使得客户对单个对象和组合对象的使用具有一致性。
所谓的根节点、树枝节点、叶子节点如上图所示。在实际项目中,如果严格区分三个节点类型,那么对于客户端调用而言,就必须辨认各个节点类型。 组合模式的核心就是:让所有的节点都实现同一个接口,让所有节点操作都具备一致性,这样就能给客户端调用带来极大的便利。
在代码实现上,组合模式分为两种:
- 透明模式
- 安全模式
透明模式:
树节点和根节点全部具备一致行为,如下图所示
我们看见抽象接口(IComponent)中直接包含了Add、Remove、GetChild方法,其实这些方法对于叶子节点来说是没有的。那么我们在具体实现的时候,这些方法在必须实现的情况下只能使用throw new NotImplementedException()来标记或者直接为空实现,这样会带来一些安全隐患,而且不符合接口隔离原则。但是这样的一个好处是客户端无需分辨是叶子还是树枝节点。
安全模式:
将树枝节点和叶子节点的不同行为逻辑分开,如下图
安全模式下,在抽象节点(IComponent)中只需要定义基础行为,树枝节点本身的操作,如Add、Remove、GetChild直接放在树枝节点中。 叶子节点无需实现树枝节点的逻辑操作。这样可以把树枝节点和叶子节点都不同行为完全分开,符合单一职责原则、接口隔离原则。但是对于客户端来说,就要稍微区分下是树枝节点还是叶子节点了。
1、场景描述
组合模式使用的场景一般都有一种包含的关系,我们使用组合模式来实现文件、文件夹的包含关系,使用安全模式。
2、实现
/// <summary>/// 抽象节点(IComponent)/// </summary>public interface IDirectory{/// <summary>/// 显示/// </summary>void Show();}/// <summary>/// 文件(叶子节点)/// </summary>public class File: IDirectory{private string fileName;public File(string fileName){this.fileName = fileName;}public void Show(){Console.WriteLine($"这是一个文件,文件名:${fileName}");}}/// <summary>/// 文件夹/// </summary>public class Folder : IDirectory{private string _folderName;private List<IDirectory> _childrens = new List<IDirectory>();public Folder(string folderName){_folderName = folderName;}/// <summary>/// 添加/// </summary>/// <param name="directory"></param>public void Add(IDirectory directory){_childrens.Add(directory);}/// <summary>/// 移除/// </summary>/// <param name="directory"></param>public void Remove(IDirectory directory){if (_childrens.Contains(directory))_childrens.Remove(directory);}/// <summary>/// 获取子节点/// </summary>/// <returns></returns>public List<IDirectory> GetChildren(){return _childrens;}public void Show(){Console.WriteLine($"这是一个文件夹:{_folderName}");foreach (var item in _childrens){item.Show();}}}// 客户端调用Folder folder1 = new Folder("文件夹1");Folder folder2 = new Folder("文件夹2");Folder folder3 = new Folder("文件夹3");File file1 = new File("文件1");File file2 = new File("文件2");File file3 = new File("文件3");File file4 = new File("文件4");folder1.Add(file1);folder1.Add(folder2);folder2.Add(folder3);folder2.Add(file2);folder3.Add(file3);folder3.Add(file4);folder1.Show();Console.WriteLine("***********************");folder3.Remove(file3);folder1.Show();
3、优缺点
优点:
- 屏蔽各层次之间的差异,使得客户端可以一致的处理不同层次,不用关心差异。
- 组合(树枝节点)加入新的节点,不会更改源代码,符合“开闭原则”
缺点:
