大名鼎鼎的套娃模式
组合模式有时又称部分-整体模式(Part-Whole)
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。
——《设计模式》GoF
组合模式的实现根据所实现接口的区别分为两种形式,分别称为安全模式和透明模式。
安全式的组合模式要求管理组合对象的方法只出现在树枝构件类中,而不出现在树叶构件中。
组合模式可以不提供父对象的管理方法,但组合模式必须在合适的地方提供子对象的管理方法(诸如:add、remove、getChild等)
在Composite类里面声明所有的用来管理子类对象的方法。这样的做法是安全的做法,因为树叶类型的对象根本就没有管理子类对象的方法,因此,如果客户端对树叶类对象使用这些方法时,程序会在编译时期出错。
这个选择的缺点是不够透明,因为树叶类和组合类将具有不同的接口。
namespace SafeComposite
{
public abstract class Component
{
// Fields
protected string name;
// Constructors
public Component(string name)
{
this.name = name;
}
// Operation
public abstract void Display(int depth);
}
}
namespace SafeComposite
{
class Composite : Component
{
// Fields
private ArrayList children = new ArrayList();
// Constructors
public Composite(string name) : base(name) { }
// Methods for managing--分支节点专有的方法
public void Add(Component component)
{
children.Add(component);
}
public void Remove(Component component)
{
children.Remove(component);
}
public override void Display(int depth)
{
Console.WriteLine(new String('-', depth) + name);
// Display each of the node's children
foreach (Component component in children)
component.Display(depth + 2);
}
}
}
namespace SafeComposite
{
class Leaf : Component
{
// Constructors
public Leaf(string name) : base(name) { }
// Methods
public override void Display(int depth)
{
Console.WriteLine(new String('-', depth) + name);
}
}
}
namespace SafeComposite
{
class Program
{
static void Main(string[] args)
{
// 注意此处不能使用Component,因为它不能对子节点进行管理操作
Composite root = new Composite("root");
root.Add(new Leaf("Leaf A"));
root.Add(new Leaf("Leaf B"));
Composite comp = new Composite("Composite X");
comp.Add(new Leaf("Leaf XA"));
comp.Add(new Leaf("Leaf XB"));
root.Add(comp);
root.Add(new Leaf("Leaf C"));
// Add and remove a leaf
Leaf l = new Leaf("Leaf D");
//l.Add(new Leaf("xxxxxx")); //Compiling Error!!!
root.Add(l);
root.Remove(l);
// Recursively display nodes
root.Display(0);
Console.ReadLine();
}
}
}
透明式的组合模式要求所有的具体构件类,不论树枝构件还是树叶构件,均符合一个固定的接口。
在Component里面声明所有的用来管理子类对象的方法,包括add()、remove(),以及getChild()方法。这样做的好处是所有的构件类都有相同的接口。在客户端看来,树叶类对象与组合类对象的区别起码在接口层次上消失了,客户端可以同等的对待所有的对象。
这种方式的缺点是不够安全,因为树叶类对象和组合类对象在本质上是有区别的。树叶类对象不可能有下一个层次的对象,因此add()、remove()以及getChild()方法没有意义,这样做即便在编译时期不会出错,但是在运行时期会出错。
namespace TransparentComposite
{
public abstract class Component
{
// Fields
protected string name;
// Constructors
public Component(string name)
{ this.name = name; }
// 子类对象的管理方法
abstract public void Add(Component c);
abstract public void Remove(Component c);
//与业务相关的操作
abstract public void Display(int depth);
}
}
namespace TransparentComposite
{
class Composite : Component
{
// Fields
private ArrayList children = new ArrayList();
// Constructors
public Composite(string name) : base(name) { }
// Methods
public override void Add(Component component)
{ children.Add(component); }
public override void Remove(Component component)
{ children.Remove(component); }
public override void Display(int depth)
{
Console.WriteLine(new String('-', depth) + name);
// Display each of the node's children
foreach (Component component in children)
component.Display(depth + 2);
}
}
}
namespace TransparentComposite
{
class Leaf : Component
{
// Constructors
public Leaf(string name) : base(name) { }
// Methods
public override void Add(Component c)
{ Console.WriteLine("Cannot add to a leaf"); }
public override void Remove(Component c)
{ Console.WriteLine("Cannot remove from a leaf"); }
public override void Display(int depth)
{ Console.WriteLine(new String('-', depth) + name); }
}
}
namespace TransparentComposite
{
class Program
{
static void Main(string[] args)
{
// Create a tree structure
Component root = new Composite("root");
root.Add(new Leaf("Leaf A"));
root.Add(new Leaf("Leaf B"));
Component comp = new Composite("Composite X");
comp.Add(new Leaf("Leaf XA"));
comp.Add(new Leaf("Leaf XB"));
root.Add(comp);
root.Add(new Leaf("Leaf C"));
// Add and remove a leaf
Component l = new Leaf("Leaf D");
l.Add(new Leaf("xxxx")); //Executing Error!!!
root.Add(l);
root.Remove(l);
// Recursively display nodes
root.Display(0);
Console.ReadLine();
}
}
}
Composite模式采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转化为“一对一”的关系,使得客户代码可以一致地处理对象和对象容器,无需关心处理的是单个的对象,还是组合的对象容器。
将“客户端代码与复杂的对象容器结构”解耦是Composite模式的核心思想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的复杂内部实现结构——发生依赖关系,从而更能“应对变化”。
Composite模式中,是将“Add和Remove等和对象容器相关的方法”定义在“表示抽象对象的Component类”中,还是将其定义在“表示对象容器的Composite类”中,是一个关乎“透明性”和“安全性”的两难问题,需要仔细权衡。透明方式违背面向对象的“单一职责原则”,但是对于这种特殊结构,这又是必须付出的代价。
微软在.Net中采用了另外的方式来处理这个两难问题: Controls
示例: DotNetComposite
namespace DotNetComposite
{
/// <summary>
/// 抽象节点
/// </summary>
abstract class Component
{
/// <summary>
/// 子节点列表
/// </summary>
public List<Component> Children;//利用此列表来屏蔽安全性和透明性问题--客户端程序直接对此表进行添加和删除
// Fields
protected string name;
// Constructors
public Component(string name)
{
this.name = name;
Children = new List<Component>();//创建子节点列表--叶子和分支统一对待--区别是叶子节点的列表肯定为空
}
// Operation
public abstract void Display(int depth);
}
}
namespace DotNetComposite
{
/// <summary>
/// 分支节点
/// </summary>
class Composite:Component
{
// Constructors
public Composite(string name) : base(name) { }
public override void Display(int depth)
{
Console.WriteLine(new String('-', depth) + name);
//此处需要判断是否含有字节点--有的话,需要依次处理
if(Children.Count>0)
foreach (Component component in Children)
component.Display(depth + 2);
}
}
}
namespace DotNetComposite
{
/// <summary>
/// 叶子节点
/// </summary>
class Leaf:Component
{
// Constructors
public Leaf(string name) : base(name) { }
//此处只处理叶子节点的数据,不会理会是否含有子节点(应该没有子节点)
public override void Display(int depth)
{
Console.WriteLine(new String('-', depth) + name);
}
}
}
namespace DotNetComposite
{
class Program
{
static void Main(string[] args)
{
Component root = new Composite("root");
root.Children.Add(new Leaf("Leaf A"));
root.Children.Add(new Leaf("Leaf B"));
Component comp = new Composite("Composite X");
comp.Children.Add(new Leaf("Leaf XA"));
comp.Children.Add(new Leaf("Leaf XB"));
root.Children.Add(comp);
root.Children.Add(new Leaf("Leaf C"));
// Add and remove a leaf
Leaf l = new Leaf("Leaf D");
l.Children.Add(new Leaf("xxxxxx")); //此处添加子节点无意义--添加不会出任何问题,但是无用--处理方法中不含有处理子节点的代码
root.Children.Add(l);
root.Children.Remove(l);
// Recursively display nodes
root.Display(0);
Console.ReadLine();
}
}
}
可以看出,将Composite类中的集合类型的数据结构放在抽象类中,使叶子类也具有这个集合,但是增删子节点对叶子类不作任何操作,可以做到在客户端透明操作,又不会报错。
本文地址:https://blog.csdn.net/qq_41342326/article/details/107481091
如对本文有疑问, 点击进行留言回复!!
网友评论