1. 意图
将对象组合成树形结构以表示“部分-整体” 的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。
Composite模式的关键是一个抽象类,它既可以代表图元,又开始代表图元的容器。
2.适用性
在以下情况使用Composite模式
你想表示对象的部分-整体层次结构
你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
3.类图
4. 参与着
Component, Leaf, Composite, Client
5. Composite的目的是为了为调用者提供操作的一致性,因此Leaf与Composite都继承自抽象的Component
要注意的是.NET Framework 中的 XMLNode ,TreeNode 提供的容器对象 并不继承自同样的接口,因此不是Composite模式
注意上图的add, remove和getChild()方法,通常这些被认为是声明管理子部件的操作,问题是对于Leaf对象,这些操作并没有意义
此时有两种办法:
1) 将add() remove()等操作定义在Composite中,不定义在Component中, 这种做法被称为 安全性
这种做法的缺点是Leaf 和 Composite有不同的接口,因此调用者必须知道调用的对象是Leaf还是Composite,做一个类型转换
2) 将add() remove() 等操作定义在 Component中, 这种做法被称为 透明性
缺点是add , remove 对象可能会导致一些无意义的操作
通常会更加强调透明性(这正是Comosite模式的主要目的), 当然我们需要用一些技巧来避免透明性带来的缺点。
6. 实例
考虑以下的一个场景,某个大规模集团军的战斗
class Program { static void Main(string[] args) { } } interface ICommand { void Attack(); void Add(ICommand command); void Remove(ICommand command); string Name { get; set; } ICommand Owner { get; set; } List<ICommand> GetChilds(); ICommand GetArmy(); } class Army : ICommand { protected List<ICommand> cmdList = null; public Army(ICommand owner, string name) { this.Name = name; cmdList = new List<ICommand>(); } public string Name { get; set; } public ICommand Owner { get; set; } public void Attack() { Console.WriteLine(Name + " joins the battle"); foreach (ICommand cmd in cmdList) cmd.Attack(); } public void Add(ICommand command) { command.Owner = this; cmdList.Add(command); Console.WriteLine(command.Name + " joins the " + Name); } public void Remove(ICommand command) { if (cmdList.IndexOf(command) > -1) cmdList.Remove(command); Console.WriteLine(command.Name + " leaves the " + Name); } public ICommand GetArmy() { return this; } public List<ICommand> GetChilds() { return cmdList; } } class Soldier : ICommand { public Soldier(ICommand owner, string name) { if (owner == null) throw new NotSupportedException("soldier must belong to an army"); this.Name = name; this.Owner = owner; } public string Name { get; set; } public ICommand Owner { get; set; } public void Attack() { Console.WriteLine(Name + " joins the battle"); } public void Add(ICommand command) { } public void Remove(ICommand command) { } public List<ICommand> GetChilds() { return null; } public ICommand GetArmy() { return null; } }
上面的代码没有调用者的部分,接下来讨论调用者
如果根据需要去创建不同的对象,然后add, remove ... 是一种方法,但是并不优雅
这个时候可以考虑Builder模式, Builder模式定义对象创建的步骤,使得不同的过程可以有不同的表示,非常适合Composite