一、引言
在软件开发中,我们经常会遇到处理简单对象和复合对象的情况,例如操作系统中文件目录的处理,目录可以包含单独的文件,也可以包括文件夹,而文件夹里又包含着文件,如此递归下去或者说是分级数据结构。由于简单对象和复合对象在功能上的区别,导致在操作过程中必须区分简单对象和复合对象,这样导致客户端调用时带来不必要的麻烦,作为客户,希望能够始终如一的对待简单对象和复杂对象。组合模式就是解决这个问题的
二、组合模式
定义:将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
来看看组合模式的基本代码结构
abstract class Component { protected string name; public Component(string name) { this.name = name; } public abstract void Add(Component component); public abstract void Remove(Component component); public abstract void Display(int depth); }
//Leaf在组合中表示叶节点对象 class Leaf : Component { public Leaf(string name) : base(name) { } //由于叶子没有在增加分枝和树叶,所以ADD与Remove方法实现它没有意义; //但可以消除叶节点和枝节点在抽象层次的区别,它们具备完全一致的接口 public override void Add(Component component) { Console.WriteLine("叶节点不允许添加树枝树叶节点"); } public override void Display(int depth) { Console.WriteLine(new String(‘-‘,depth)+name); } public override void Remove(Component component) { Console.WriteLine("由于叶节点没有子节点,这里移除没有任何意义"); } }
class Composite : Component { private List<Component> Children = new List<Component>(); public Composite(string name) : base(name) { } public override void Add(Component component) { Children.Add(component); } //显示枝节点名称并对其下级进行遍历 public override void Display(int depth) { Console.WriteLine(new String(‘-‘,depth)+name); foreach (Component component in Children) { component.Display(depth + 2); } } public override void Remove(Component component) { Children.Remove(component); } }
static void Main(string[] args) { 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); Composite comp2 = new Composite("Composite XY"); comp2.Add(new Leaf("Leaf XYA")); comp2.Add(new Leaf("Leaf XYB")); comp.Add(comp2); root.Add(new Leaf("Leaf C")); Leaf leaf = new Leaf("Leaf D"); root.Add(leaf); root.Remove(leaf); root.Display(0); Console.Read(); }
结果如下图所示:
透明方式:在Component中声明所有管理子对象的方法,这样实现Component的子类都有Add和Remove方法,这样做的好处是叶节点和枝节点对于外界没有区别,它们具备完全一致的接口,但问题也很明显,Leaf本身没有Add和Remove方法,实现它是没有意义的
安全方式:在Component中不去声明Add和Remove方法,那么子类Leaf就不需要实现它,而是在Composite中声明所有管理子类的方法,不过由于不够透明,树叶类和树枝类不具有相同的接口,客户端调用需要相应的判断,带来了不便
下面是大话设计模式中公司管理系统的例子:
public abstract class Company { protected string Name; public Company(string name) { this.Name = name; } public abstract void Add(Company company); public abstract void Remove(Company company); public abstract void Display(int depth); public abstract void LineOfDuty(); }
//具体公司类 树枝节点 class ConcreteCompany : Company { //一个子对象集合 用来存储其下属的枝节点 叶节点 private List<Company> Children = new List<Company>(); public ConcreteCompany(string name) : base(name) { } public override void Add(Company company) { Children.Add(company); } public override void Display(int depth) { Console.WriteLine(new String(‘-‘,depth)+Name); foreach (Company com in Children) { com.Display(depth+2); } } public override void LineOfDuty() { foreach (Company com in Children) { com.LineOfDuty(); } } public override void Remove(Company company) { Children.Remove(company); } }
//人力资源部类 树叶节点 class HRDepartment:Company { public HRDepartment(string name) :base(name) { } public override void Add(Company company) { } public override void Display(int depth) { Console.WriteLine(new String(‘-‘,depth)+Name); } public override void LineOfDuty() { Console.WriteLine($"{Name}员工招聘培训管理"); } public override void Remove(Company company) { } }
//财务部类 树叶节点 class FinanceDepartment : Company { public FinanceDepartment(string name) : base(name) { } public override void Add(Company company) { } public override void Display(int depth) { Console.WriteLine(new String(‘-‘,depth)+Name); } public override void LineOfDuty() { Console.WriteLine($"{Name}公司财务收支管理"); } public override void Remove(Company company) { } }
static void Main(string[] args) { ConcreteCompany root = new ConcreteCompany("北京总公司"); root.Add(new HRDepartment("总公司人力资源部")); root.Add(new FinanceDepartment("总公司财务部")); ConcreteCompany comp = new ConcreteCompany("上海华东分公司"); comp.Add(new HRDepartment("华东分公司人力资源部")); comp.Add(new FinanceDepartment("华东分公司财务部")); root.Add(comp); ConcreteCompany comp1 = new ConcreteCompany("南京分公司"); comp1.Add(new HRDepartment("南京人力资源部")); comp1.Add(new FinanceDepartment("南京财务部")); comp.Add(comp1); ConcreteCompany comp2 = new ConcreteCompany("杭州分公司"); comp2.Add(new HRDepartment("杭州人力资源部")); comp2.Add(new FinanceDepartment("杭州财务部")); comp.Add(comp2); Console.WriteLine("\n结构图"); root.Display(0); Console.WriteLine("\n职责图"); root.LineOfDuty(); Console.Read(); }
运行结果如下图:
使用场景:
1.想表示对象部分-整体层次结构;
2.希望用户忽略单个对象和组合对象的不同,统一的使用组合结构中的所有对象。
优点:
1.高层模块不需要关心处理的是单个对象还是复合对象;客户程序可以像处理单个对象一样处理复合对象,将客户程序与复合对象容器结构解耦
2.更容易往组合对象中添加新的构件,只需要找父节点即可
缺点:设计更加复杂,使得客户需要更多时间理清类之间的层次关系
关于组合模式的学习就到此结束了,希望能够帮到你,若有不足,欢迎斧正,感谢您的阅读。