组合模式
组合模式属于结构型模式,组合多个对象形成树形结构以表示具有部分-整体关系的层次结构。组合模式让系统可以统一对待单个对象和组合对象。
组合模式关注那些包含叶子构件和容器构件的结构以及它们的组织形式,在叶子构件中不包含成员对象,而容器构件中可以包含成员对象,这些对象通过递归组合可构成一个树形结构。
组合模式的结构
- Component(抽象构建):接口或抽象类,为叶子构件和容器构件提供接口,可以包含所有子类共有行为的声明。在抽象构件中声明了访问以及管理它的子构件的方法。
- Leaf(叶子构件):叶子节点对象,叶子节点不存在子节点,它只实现了抽象构件中定义的行为。对于其它方法可以采取抛出错误信息来处理。
- Composite(容器构件):容器节点对象,容器节点包含子节点,子节点可以是叶子节点也可以是容器节点。它提供一个集合用来存储子节点,实现了抽象构件中声明的所有方法。
组合模式的实现
组合模式的实现关键点在于定义了一个既可以表示叶子构件又可以表示容器构件的抽象层,通过对抽象层进行编程,无须知道对象具体是什么构件,可以统一进行处理。其次容器对象与抽象构件之间是聚合关联关系,在容器对象中既可以包含叶子又可以包含容器,以此实现递归组合,形成树形结构。
组合模式设计代码如下
//抽象层
abstract class Component
{
public abstract void Add(Component c);
public abstract void Remove(Component c);
public abstract Component GetChild(int i);
public abstract void Operation();
}
//叶子节点
class Leaf : Component
{
//实现这些叶子节点不需要的方法增加实现复杂度
public override void Add(Component c)
{
//...异常处理或错误提示
}
public override Component GetChild(int i)
{
//...异常处理或错误提示
return null;
}
public override void Remove(Component c)
{
//...异常处理或错误提示
}
public override void Operation()
{
//具体业务方法的实现
Console.WriteLine("叶子节点具体业务方法的实现");
}
}
//容器节点
class Composite : Component
{
private List<Component> m_Childs = new List<Component>();
public override void Add(Component c)
{
this.m_Childs.Add(c);
}
public override Component GetChild(int i)
{
return this.m_Childs[i];
}
public override void Remove(Component c)
{
this.m_Childs.Remove(c);
}
public override void Operation()
{
foreach (var item in this.m_Childs)
item.Operation();
}
}
示例场景
使用组合模式设计一个界面控件库,界面控件分为两类,单元控件和容器空间,单元空间包括文本、按钮等,容器空间包括窗体、面板等。
代码
//抽象构件
abstract class AbstractComponent
{
public abstract void Add(AbstractComponent component);
public abstract void Remove(AbstractComponent component);
public abstract AbstractComponent GetChild(int index);
public abstract void Redraw();
}
//文本控件(叶子节点)
class TextComponent : AbstractComponent
{
private string m_Name;
public TextComponent(string name)
{
this.m_Name = name;
}
public override void Add(AbstractComponent component)
{
throw new Exception("文本控件不支持该方法");
}
public override AbstractComponent GetChild(int index)
{
throw new Exception("文本控件不支持该方法");
}
public override void Remove(AbstractComponent component)
{
throw new Exception("文本控件不支持该方法");
}
public override void Redraw()
{
Console.WriteLine("重绘------{0}", this.m_Name);
}
}
//按钮控件(叶子节点)
class ButtonComponent : AbstractComponent
{
private string m_Name;
public ButtonComponent(string name)
{
this.m_Name = name;
}
public override void Add(AbstractComponent component)
{
throw new Exception("按钮控件不支持该方法");
}
public override AbstractComponent GetChild(int index)
{
throw new Exception("按钮控件不支持该方法");
}
public override void Remove(AbstractComponent component)
{
throw new Exception("按钮控件不支持该方法");
}
public override void Redraw()
{
Console.WriteLine("重绘------{0}", this.m_Name);
}
}
//窗体控件(容器节点)
class FormsComponent : AbstractComponent
{
private string m_Name;
private List<AbstractComponent> m_Childs;
public FormsComponent(string name)
{
this.m_Name = name;
this.m_Childs = new List<AbstractComponent>();
}
public override void Add(AbstractComponent component)
{
this.m_Childs.Add(component);
}
public override AbstractComponent GetChild(int index)
{
return this.m_Childs[index];
}
public override void Remove(AbstractComponent component)
{
this.m_Childs.Remove(component);
}
public override void Redraw()
{
Console.WriteLine("重绘------{0}", this.m_Name);
foreach (var item in this.m_Childs)
item.Redraw();
}
}
//面板控件(容器节点)
class PanelComponent : AbstractComponent
{
private string m_Name;
private List<AbstractComponent> m_Childs;
public PanelComponent(string name)
{
this.m_Name = name;
this.m_Childs = new List<AbstractComponent>();
}
public override void Add(AbstractComponent component)
{
this.m_Childs.Add(component);
}
public override AbstractComponent GetChild(int index)
{
return this.m_Childs[index];
}
public override void Remove(AbstractComponent component)
{
this.m_Childs.Remove(component);
}
public override void Redraw()
{
Console.WriteLine("重绘------{0}", this.m_Name);
foreach (var item in this.m_Childs)
item.Redraw();
}
}
//使用
static void Main()
{
var forms = new FormsComponent("我的窗体");
var panel1 = new PanelComponent("我的界面");
var panel2 = new PanelComponent("子界面");
var text1 = new TextComponent("标题文本");
var text2 = new TextComponent("内容文本");
var button1 = new ButtonComponent("关闭按钮");
var button2 = new ButtonComponent("刷新按钮");
panel2.Add(text1);
panel2.Add(text2);
panel2.Add(button1);
panel1.Add(panel2);
panel1.Add(text1);
panel1.Add(button1);
panel1.Add(button2);
forms.Add(panel1);
forms.Add(text1);
forms.Add(button1);
//重绘我的窗体
forms.Redraw();
Console.ReadKey();
}
运行结果
透明组合模式与安全组合模式
透明组合模式
上述所讲的是透明组合模式,在透明组合模式中抽象构件中声明了子类的所有公共方法,这样做的好处是所有子类都具有相同的接口,可以将叶子节点与容器节点统一处理。
其缺点是不够安全,因为叶子节点不存在下一层,和容器节点有本质上的差别,因此为叶子节点提供Add、Remove等方法是没有意义的。
安全组合模式
安全组合模式中,抽象构件将不再声明任何用于管理成员对象的方法,而是在Composite声明这些方法。对于叶子节点将调用不到这些方法,所以这样做是更安全的。
其缺点也很明显,就是不够透明,因为叶子节点与容器节点存在差异,因此不再能够完全针对抽象层编程,必须区别对待叶子节点与容器节点。
安全组合模式结构
组合模式的优点
- 能够清楚地定义分层次的复杂对象,表示对象的全部或部分层次,从而使用时可以忽略层次的差异,方便对整个层次结构进行控制。
- 可以一直地使用一个组合结构或是其中的一个单元,不必关心处理对象的类型,简化代码。
- 增加新的容器构件与叶子构件都很方便,无需修改现有代码,符合开闭原则。
- 为树形结构的面向对象提供了一种灵活的解决方案,通过叶子节点和容器节点的递归组合可以形成复杂的树形结构,但是对树形结构的控制非常简单。
组合模式的缺点
在使用组合模式时,其叶子节点和容器节点都是具体的实现类,而不是接口,违反了依赖倒置原则。
应用场景
- 在具有整体和部分的结构中,希望能够忽略整体与部分的差异进行统一处理。
- 处理树形结构。
- 在一个系统中能够分离出叶子节点与容器节点,且它们的类型不固定(后续可能会增加新类型)。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。