Head First 设计模式 11-组合模式
组合模式 允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
组合模式让我们能用 树形方式 创建对象的结构,树里面包含了组合以及个别的对象。
使用组合结构,我们能把 相同的操作 应用在 组合 和 个别对象 上。换句话说,在大多数情况下,我们可以忽略对象组合和个别对象之间的差别。
之所以基类(Component)是抽象类(abstract class
),是因为树形结构中 叶节点(Leaf)只需要实现具体的操作,而 子节点(Composite)则要实现所有的方法。同时,为了防止意外的访问了叶节点不应支持的方法,基类中虚方法的默认实现中抛出了 InvalidOperationException 。
示例代码
Component
public abstract class Component
{
public virtual void Operation()
{
throw new InvalidOperationException();
}
public virtual void Add(Component component)
{
throw new InvalidOperationException();
}
public virtual void Remove(Component component)
{
throw new InvalidOperationException();
}
public virtual Component GetChild(int index)
{
throw new InvalidOperationException();
}
}
Leaf
public class Leaf : Component
{
public override void Operation()
{
Console.WriteLine("a leaf do some operation.");
}
}
Composite
public class Composite : Component
{
List<Component> _components = new List<Component>();
public override void Add(Component component)
{
_components.Add(component);
}
public override Component GetChild(int index)
{
return _components[index];
}
public override void Operation()
{
Console.WriteLine("a composite do some operation.");
foreach (var component in _components)
{
component.Operation();
}
}
public override void Remove(Component component)
{
_components.Remove(component);
}
}
Client
Composite subComposite1 = new Composite();
subComposite1.Add(new Leaf());
subComposite1.Add(new Leaf());
Composite subComposite2 = new Composite();
subComposite2.Add(new Leaf());
Composite composite = new Composite();
composite.Add(subComposite1);
composite.Add(subComposite2);
composite.Operation();
设计原则
本节没有新的设计原则,但是却破坏了上一节中出现的 单一职责 原则。 Composite 类兼具两种类型的操作:迭代 和 叶节点的业务操作,可能会导致叶节点错误的执行了子节点的操作,失去了一些“安全性”。这是设计上的抉择,牺牲了安全性,增加了透明性。
什么是透明性? 通过让组件的接口同时包含一些管理子节点和叶节点的操作,客户就可以将子节点和叶节点一视同仁。也就是说,一个元素究竟是组合还是叶节点,对客户来说是透明的。
尽管我们受到设计原则的指导,但是,我们总是需要观察某原则对我们的设计所造成的影响。有时候,我们会故意做一些违反原则的事情。
组合迭代器
如果需要遍历树结构的所有节点,就需要实现一个 组合迭代器(CompositeEnumerator) ,另外在叶节点上还会用到 空迭代器(NullEnumerator) 。
需要 Componenet 类继承 IEnumerable<Component>
接口, Composite 和 Leaf 需要返回各自的迭代器。
其类图如下:
Component
public abstract class Component: IEnumerable<Component>
{
public virtual void Operation()
{
throw new InvalidOperationException();
}
public virtual void Add(Component component)
{
throw new InvalidOperationException();
}
public virtual void Remove(Component component)
{
throw new InvalidOperationException();
}
public virtual Component GetChild(int index)
{
throw new InvalidOperationException();
}
public virtual IEnumerator<Component> GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
Leaf
public class Leaf : Component
{
public override void Operation()
{
Console.WriteLine("a leaf do some operation.");
}
public override IEnumerator<Component> GetEnumerator()
{
return new NullEnumerator();
}
}
Composite
public class Composite : Component
{
List<Component> _components = new List<Component>();
public override void Add(Component component)
{
_components.Add(component);
}
public override Component GetChild(int index)
{
return _components[index];
}
public override void Operation()
{
Console.WriteLine("a composite do some operation.");
foreach (var component in _components)
{
component.Operation();
}
}
public override void Remove(Component component)
{
_components.Remove(component);
}
public override IEnumerator<Component> GetEnumerator()
{
return _components.GetEnumerator();
}
}
CompositeEnumerator
public class CompositeEnumerator : IEnumerator<Component>
{
Stack<IEnumerator<Component>> _stack = new Stack<IEnumerator<Component>>();
private List<Component> _components;
public CompositeEnumerator(List<Component> components)
{
_components = components;
_stack.Push(_components.GetEnumerator());
}
public Component Current { get; private set; }
object IEnumerator.Current => Current;
public void Dispose()
{
_stack.Clear();
_stack = null;
_components = null;
}
public bool MoveNext()
{
if (!_stack.TryPeek(out var enumerator))
{
return false;
}
if (!enumerator.MoveNext())
{
_stack.Pop();
return MoveNext();
}
else
{
Current = enumerator.Current;
if (Current is Composite)
{
_stack.Push(Current.GetEnumerator());
}
return true;
}
}
public void Reset()
{
_stack.Clear();
_stack.Push(_components.GetEnumerator());
}
}
NullEnumerator
public class NullEnumerator : IEnumerator<Component>
{
public Component Current => default(Component);
object IEnumerator.Current => Current;
public void Dispose()
{
}
public bool MoveNext()
{
return false;
}
public void Reset()
{
}
}
Client
Composite subComposite1 = new Composite();
subComposite1.Add(new Leaf());
subComposite1.Add(new Leaf());
Composite subComposite2 = new Composite();
subComposite2.Add(new Leaf());
subComposite2.Add(subComposite1);
Composite composite = new Composite();
composite.Add(subComposite2);
List<Composite> composites = new List<Composite>();
composites.Add(composite);
foreach (var item in composites)
{
item.Operation();
}