人物:小菜,大鸟
事件:小菜遇到了一个难题,他的公司开发了一个OA系统,然后甲方希望在分公司推广,共用同一套系统,于是大鸟推出了组合模式,传给了小菜,解决了问题。
组合模式:
1.简述了组合模式,阐述了原理,实现,还有部分疑问答疑
2.用组合模式解决小菜的问题实例
组合模式
1.概念:将对象组合成树形结构以表示'部分-整体'的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性
2.结构图:
3.代码如下:
Component类,是组合中的对象声明接口,在适当情况下,实现所有类共有的默认行为。声明一个接口用于访问和管理Component的子部件:
public abstract class Component { protected String name; public Component(String name) { this.name = name; } public abstract void add(Component c); public abstract void remove(Component c); public abstract void display(int depth); }
Leaf类,在组合中表示叶节点对象,叶节点没有子节点,所以add和remove实现没有意义,如下的实现实现方式可以消除叶节点和枝节点对象在抽象层次的区别,它们就可以具备完全一致的接口:
@Slf4j public class Leaf extends Component { public Leaf(String name) { super(name); } @Override public void add(Component c) { log.info("cannot add to a leaf"); } @Override public void remove(Component c) { log.info("cannot remove from a leaf"); } @Override public void display(int depth) { log.info(depth + name); } }
Composite类,定义枝节点行为,用来存储子部件,在Component接口中实现与子部件有关的操作,比如进行add和remove:
@Slf4j public class Composite extends Component { private List<Component> children = new ArrayList<>(); public Composite(String name) { super(name); } @Override public void add(Component c) { children.add(c); } @Override public void remove(Component c) { children.remove(c); } @Override public void display(int depth) { log.info(depth + name); for (Component component : children) { component.display(depth + 2); } } }
客户端代码:
public class ComponentClient { public static void main(String[] args) { 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); Component 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(1); } }
输出结果:
1root
3Leaf A
3Leaf B
3Composite X
5Leaf XA
5Leaf XB
5Composite XY
7Leaf XYA
7Leaf XYB
3Leaf C
展示如下:
4.透明方式与安全方式
(1)问:叶子节点不是不能再分枝么,为什么Leaf类中还有add和remove
答:现在的处理方式叫做透明方式。这样在Component中会声明所有用来管理子对象的方法,其中包括add,remove等,这样Component接口的所有子类都具备了add和remove,所以好处就是使得叶子节点和枝节点对于外界就没有区别了,它们具备完全一致的行为接口,当然问题也很明显,因为Leaf类本身不具备add和remove,所以这两个方法的实现没有意义。
(2)问:在1问中所说的问题,那我不希望做无用功,可以add和remove方法么?
答:可以,那就需要安全方式。就不在Component接口中实现add和remove方法,而是在Composite中实现,这样虽然解决了这个问题,但由于不够透明,所以树叶树枝类不再具有相同的接口,导致客户端的调用需要加入判断,带来了不便。
5.什么时候选择用组合模式呢?
答:当需求中是体现整体和部分层次的结构时,以及你希望用户可以忽略组合对象和单个对象的不同,统一地使用组合结构中的所有对象时,就该考虑用组合模式了。
结合组合模式实现公司OA系统
1.代码结构图:
2.代码实现:
公司类,抽闲类或接口:
public abstract class Company { protected String name; public Company(String name) { this.name = name; } /** * 增加 * * @param c */ public abstract void add(Company c); /** * 移除 * * @param c */ public abstract void remove(Company c); /** * 展示 * * @param depth */ public abstract void display(int depth); /** * 履行职责 */ public abstract void lineOfDuty(); }
ConcreteCompany类,具体公司类,实现接口,树枝节点:
@Slf4j public class ConcreteCompany extends Company { private List<Company> children = new ArrayList<>(); public ConcreteCompany(String name) { super(name); } @Override public void add(Company c) { children.add(c); } @Override public void remove(Company c) { children.remove(c); } @Override public void display(int depth) { log.info(depth + name); for (Company component : children) { component.display(depth + 2); } } @Override public void lineOfDuty() { for (Company component : children) { component.lineOfDuty(); } } }
HRDepartment类和FinanceDepartment类,人力资源部类和财务部类,叶子节点:
@Slf4j public class HRDepartment extends Company { public HRDepartment(String name) { super(name); } @Override public void add(Company c) { } @Override public void remove(Company c) { } @Override public void display(int depth) { log.info(depth + name); } @Override public void lineOfDuty() { log.info("{}员工招聘培训机构", name); } }
@Slf4j public class FinanceDepartment extends Company { public FinanceDepartment(String name) { super(name); } @Override public void add(Company c) { } @Override public void remove(Company c) { } @Override public void display(int depth) { log.info(depth + name); } @Override public void lineOfDuty() { log.info("{}公司财务收支管理", name); } }
客户端调用:
@Slf4j public class ComponentClient { public 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); log.info("结构图:"); root.display(1); log.info("职责为:"); root.lineOfDuty(); } }
组合模式的好处:
(1)用户不关心处理的是叶子节点还是组合组件,这样就不用定义组合而写判断语句了
(2)基本对象可以被组合成复杂的组合对象,而组合对象又可以被组合,这样可以一直递归下去,这样任何用到基础对象的地方就都可以用到组合对象了。