缘起
某日,小明公司最近接到一个办公管理系统的项目,并且在每个城市都有分部。这属于是很常见的OA系统,只要前期将需求分析完善好,中后期开发维护是不难的。
然而,总部公司使用后觉得很OK,想要其他城市的分公司也执行使用。但是现在的问题是,其他分公司的部门和制度没有总公司那么清晰完善。
也许可以一个城市一套代码?但是总公司不乐意了,要求总部、分部等是需要成树状结构的,不可以平行管理。
那么部门Leader想到了一个合适的设计模式–组合模式。
组合模式
组合模式(Composite):将对象组合成树形结构以表示’部门-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
结构图
Component:作为组合中的对象声明接口,在适当情况下,实现所有类共有接口的默认行为,声明一个接口用于访问和管理Component的子组件
通常使用add和remove增加或删除树枝或树叶的功能
public 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在组合中表示叶节点对象,叶子节点没有子节点,所以add和remove对于Leaf对象来说是没用的,给客户端个提示就行。
public class Leaf extends Component {
public Leaf(String name) {
super(name);
}
@Override
public void add(Component component) {
System.out.println("Cannot add to a leaf");
}
@Override
public void remove(Component component) {
System.out.println("Cannot remove from a leaf");
}
// 叶子节点展示职级和名称
@Override
public void display(int depth) {
for (int i = 0; i < depth; i++) {
System.out.print("-");
}
System.out.println(name);
}
}
- Composite定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关的操作,比如增加add和删除remove
public class Composite extends Component {
private List<Component> children = new ArrayList<>();
public Composite(String name) {
super(name);
}
@Override
public void add(Component component) {
children.add(component);
}
@Override
public void remove(Component component) {
children.remove(component);
}
@Override
public void display(int depth) {
for (int i = 0; i < depth; i++) {
System.out.print("-");
}
System.out.println(name);
// 表层级关系+2
for (Component item : children) {
item.display(depth + 2);
}
}
}
- 客户端调用,可以通过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 A"));
comp.add(new Leaf("Leaf B"));
// 顶级节点加一个枝节点
root.add(comp);
Composite comp2 = new Composite("Composite XY");
// 加两个叶子节点
comp.add(new Leaf("Leaf XYA"));
comp.add(new Leaf("Leaf XYB"));
// 二级节点再加一个枝节点
comp.add(comp2);
// 展示整个架构
root.display(1);
结果
-root
---Leaf A
---Leaf B
---Composite X
-----Leaf A
-----Leaf B
-----Leaf XYA
-----Leaf XYB
-----Composite XY
当发现需求中是体现部分与整体层次的结构时,希望用户可以忽略组合对象与单个对象的不同,统一使用组合结构中的所有对象时,就应该考虑使用组合模式了。
在java开发窗体应用的容器控件Container
继承了Component(java自带的),就有add和remove方法,所以它在上面增加控件,如Button、Label、Checkbox等就变成了很自然的事,这就是典型的组合模式的应用。
公司管理系统案例
公司类:抽象类或接口
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(); // 展示职责
}
具体公司类:实现接口、树枝节点
public class ConcreteCompany extends Company {
protected List<Company> children = new ArrayList<>();
public ConcreteCompany(String name) {
super(name);
}
@Override
public void add(Company company) {
children.add(company);
}
@Override
public void remove(Company company) {
children.remove(company);
}
@Override
public void display(int depth) {
for (int i = 0; i < depth; i++) {
System.out.print("-");
}
System.out.println(name);
for (Company child : children) {
child.display(depth + 2);
}
}
@Override
public void lineOfDuty() {
for (Company child : children) {
child.lineOfDuty();
}
}
}
- 人力资源部、财务部:叶子节点
// HR
public class HRDept extends Company {
public HRDept(String name) {
super(name);
}
@Override
public void add(Company company) {
}
@Override
public void remove(Company company) {
}
@Override
public void display(int depth) {
for (int i = 0; i < depth; i++) {
System.out.print("-");
}
System.out.println(name);
}
@Override
public void lineOfDuty() {
System.out.println(name+"员工招聘培训");
}
}
// 财务部
public class FinanceDept extends Company {
public FinanceDept(String name) {
super(name);
}
@Override
public void add(Company company) {
}
@Override
public void remove(Company company) {
}
@Override
public void display(int depth) {
for (int i = 0; i < depth; i++) {
System.out.print("-");
}
System.out.println(name);
}
@Override
public void lineOfDuty() {
System.out.println(name + "公司财务收支管理");
}
}
- 客户端
// 顶级节点
ConcreteCompany root = new ConcreteCompany("总公司");
root.add(new HRDept("总公司人力资源"));
root.add(new FinanceDept("总公司财务部"));
ConcreteCompany comp = new ConcreteCompany("某分公司");
comp.add(new HRDept("某分公司人力资源"));
comp.add(new FinanceDept("某分公司财务部"));
// 给总公司增加下级部门
root.add(comp);
ConcreteCompany comp2 = new ConcreteCompany("A办事处");
comp2.add(new HRDept("A办事处人力资源"));
comp2.add(new FinanceDept("A办事处财务部"));
comp.add(comp2);
ConcreteCompany comp3 = new ConcreteCompany("B办事处");
comp3.add(new HRDept("B办事处人力资源"));
comp3.add(new FinanceDept("B办事处财务部"));
comp.add(comp3);
System.out.println("结构图");
root.display(1);
System.out.println("职责");
root.lineOfDuty();
结构图
-总公司
---总公司人力资源
---总公司财务部
---某分公司
-----某分公司人力资源
-----某分公司财务部
-----A办事处
-------A办事处人力资源
-------A办事处财务部
-----B办事处
-------B办事处人力资源
-------B办事处财务部
职责
总公司人力资源员工招聘培训
总公司财务部公司财务收支管理
某分公司人力资源员工招聘培训
某分公司财务部公司财务收支管理
A办事处人力资源员工招聘培训
A办事处财务部公司财务收支管理
B办事处人力资源员工招聘培训
B办事处财务部公司财务收支管理