写在前面
Composite组合模式属于设计模式中比较热门的一个,相信大家对它一定不像对访问者模式那么陌生,但是知其然容易,知其所以然难,要想彻底了解它,得从它能解决的设计困境出发。毕竟,任何一个模式的提出,总是为了解决某种设计困境,让我们从一个例子出发,看看组合模式的威力吧。
从例子出发
设想我们要建立一个公司的人事体系,在一个公司里,最大的是CEO,其他的都是经理或者普通员工。经理可以有下属,普通员工没有下属,我们写出这样的代码。
普通员工类
这种员工是最基层的员工,没有下属
class IndividualEmployee //普通员工
{
public string ID { get; set; }
public void ShowStatus(int indent)
{
string str = ID;
str = str.PadLeft(ID.Length + indent, '-');
Console.WriteLine(str);
}
}
经理类
经理可以有下属,下属可能是基层员工,也可能是其他经理,因为比基层员工多了下属,所以也多了一些方法维护下属属性
class Manager //经理
{
public string ID { get; set; }
public void ShowStatus(int indent)
{
string str = ID;
str = str.PadLeft(ID.Length + indent, '-');
Console.WriteLine(str);
indent += 4;
Subordinate.ForEach(s => s.ShowStatus(indent));
SubordinateManagers.ForEach(m => m.ShowStatus(indent));
}
public List<IndividualEmployee> Subordinate = new List<IndividualEmployee>();
public List<Manager> SubordinateManagers = new List<Manager>();
//下面是经理所属的方法
public void AddSubordinate(IndividualEmployee e) { Subordinate.Add(e); }
public void AddSubordinate(Manager e) { SubordinateManagers.Add(e); }
public void RemoveSubordinate(IndividualEmployee e) { Subordinate.Remove(e); }
public void RemoveSubordinate(Manager e) { SubordinateManagers.Remove(e); }
}
公司架构类
公司架构类非常简单,只需要掌握最大的CEO,即可抓出所有公司的人事架构
class CompanyHierachy
{
public Manager CEO { get; set; }
public void ShowStatus()
{
CEO.ShowStatus(0);
}
}
运行试一下
class Program
{
static void Main(string[] args)
{
Manager CEO = new Manager() { ID = "CEO" };
Manager DevManager = new Manager() { ID = "Dev Manager" };
Manager FinanceManager = new Manager() { ID = "Finance Manager" };
FinanceManager.AddSubordinate(new IndividualEmployee() { ID = "Purchase" });
DevManager.AddSubordinate(new IndividualEmployee() { ID = "Developer" });
CEO.AddSubordinate(DevManager);
CEO.AddSubordinate(FinanceManager);
CompanyHierachy company = new CompanyHierachy() { CEO = CEO };
company.ShowStatus();
}
}
非常完美,公司架构建立成功了。
再想一下
但是想想,这样的代码真的好吗?感觉起码有两个地方我们可以改进。
- 基层员工和经理其实有太多的共性(属性和方法),可以利用抽象思维,让他们继承自同一种东西吗?
- 在经理类中我们维护了多个下属列表,如果以后再加一个实习生,是不是我们又得创建更多的列表?如果我们使用了继承,这个问题还会存在吗?
基于此,利用抽象思维让经理和员工继承自同一个类(雇员)势在必行。在抽象之后,经理类会继承自雇员并且也内含雇员列表,可能第一次见到这种包含自身父类列表的设计方式会让人心悸,但这其实是一种比较常见的设计方式。这种既有继承也有合成的结构,就是组合模式的精髓。
使用组合模式进行重构
组合模式是属于结构型设计模式,它利用类型层级和聚合层级构造更大的复合结构
心细的朋友肯定发现了,经理类有些操作是经理类独有的,这些操作我们是应该抽象到和基层员工共同的父类雇员类吗?对于这个问题,一般有两种解决方案。
透明型
在此设计中,子类方法的并集被提炼到了共有父类,哪怕这些方法对于某些子类根本不需要,这样的好处是客户端在使用的时候根本不需要知道对象纠结是哪个子类,对客户端透明,所以得名。当前设计多采用这种。
安全型
安全型设计非常保守,只会提炼子类交集的方法到父类,这样的好处是绝对安全,客户端绝对不可能在IndividualEmployee对象上面调用AddSubordinate或者RemoveSubordinate。但有时候会面临向下转型的情况。
重构后的代码
抽象出共同父类雇员类,使用透明型,所有的子类方法都提炼到这个类
abstract class Employee
{
public string ID { get; set; }
public abstract void ShowStatus(int indent);
public abstract void AddSubordinate(Employee e);
public abstract void RemoveSubordinate(Employee e);
}
对于基层员工,如果客户端无意间调用了不该使用的方法,直接抛出异常,这样能更快的暴露出客户端代码逻辑中的问题
class IndividualEmployee : Employee
{
public override void ShowStatus(int indent)
{
string str = ID;
str = str.PadLeft(ID.Length + indent, '-');
Console.WriteLine(str);
}
public override void AddSubordinate(Employee e)
{
throw new NotImplementedException();
}
public override void RemoveSubordinate(Employee e)
{
throw new NotImplementedException();
}
}
在经理类中,得益于共有父类Employee,我们可以用一个列表装下所有的下属,不论下属是基层员工,还是经理,抑或是未来可能添加的实习生。毕竟他们都是雇员嘛
class Manager : Employee
{
public override void ShowStatus(int indent)
{
string str = ID;
str = str.PadLeft(ID.Length + indent, '-');
Console.WriteLine(str);
indent += 4;
Subordinate.ForEach(s => s.ShowStatus(indent));
}
public List<Employee> Subordinate = new List<Employee>();
//下面是经理所属的方法
public override void AddSubordinate(Employee e) { Subordinate.Add(e); }
public override void RemoveSubordinate(Employee e) { Subordinate.Remove(e); }
}
公司架构类和客户端代码调用保持不变,运行结果一致,重构成功。
可以看到,在使用了组合模式之后,现在的代码不但消除了冗余,也更具有抵御未来变化的能力,同时也变得更加符合树结构。
这就是关于组合模式的介绍,希望大家喜欢。