Loading... # 设计模式(十六)——组合模式 ## 参考 > 大话设计模式  ——  程杰 著 ## 目录 [设计模式(一)——简单工厂模式](https://www.princelei.club/archives/67.html) [设计模式(二)——策略模式](https://www.princelei.club/archives/68.html) [设计模式(三)——设计原则](https://www.princelei.club/archives/116.html) [设计模式(四)——装饰模式](https://www.princelei.club/archives/117.html) [设计模式(五)——代理模式](https://www.princelei.club/archives/119.html) [设计模式(六)——工厂方法模式](https://www.princelei.club/archives/132.html) [设计模式(七)——原型模式](https://www.princelei.club/archives/133.html) [设计模式(八)——模板方法模式](https://www.princelei.club/archives/134.html) [设计模式(九)——外观模式](https://www.princelei.club/archives/135.html) [设计模式(十)——建造者模式](https://www.princelei.club/archives/136.html) [设计模式(十一)——观察者模式](https://www.princelei.club/archives/137.html) [设计模式(十二)——抽象工厂模式](https://www.princelei.club/archives/138.html) [设计模式(十三)——状态模式](https://www.princelei.club/archives/139.html) [设计模式(十四)——适配器模式](https://www.princelei.club/archives/140.html) [设计模式(十五)——备忘录模式](https://www.princelei.club/archives/141.html) [设计模式(十六)——组合模式](https://www.princelei.club/archives/147.html) [设计模式(十七)——迭代器模式](https://www.princelei.club/archives/148.html) [设计模式(十八)——单例模式](https://www.princelei.club/archives/157.html) [设计模式(十九)——桥接模式](https://www.princelei.club/archives/159.html) [设计模式(二十)——命令模式](https://www.princelei.club/archives/160.html) [设计模式(二十一)——职责链模式](https://www.princelei.club/archives/161.html) [设计模式(二十二)——中介者模式](https://www.princelei.club/archives/162.html) [设计模式(二十三)——享元模式](https://www.princelei.club/archives/163.html) [设计模式(二十四)——解释器模式](https://www.princelei.club/archives/173.html) [设计模式(二十五)——访问者模式](https://www.princelei.club/archives/174.html) ## 何为组合模式 - 组合模式(Composite),将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 ## 示例 组合模式实现公司管理系统 ``` /** * 公司类(Component) * 组合中对象声明接口,在适当情况下,实现所有类共有接口的默认行为 */ public interface Company { //通常都用add和remove方法来提供增加或移除树叶或树枝的功能 void add(Company c);//增加 void remove(Company c);//移除 void display(int depth);//显示 void lineOfDuty();//履行职责 } ``` ``` /** * 具体公司类(Composite),枝节点,用来储存子部件,在Component接口中 * 实现与子部件有关的操作,比如增加add和删除remove */ public class ConcreteCompany implements Company { private List<Company> children = new ArrayList<>(); private String name; public ConcreteCompany(String name) { this.name = 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) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < depth; i++) { sb.append("-"); } System.out.println(sb.toString() + name); children.forEach(component -> component.display(depth + 2)); } //履行职责 @Override public void lineOfDuty() { children.forEach(Company::lineOfDuty); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ConcreteCompany that = (ConcreteCompany) o; return name.equals(that.name); } @Override public int hashCode() { return name.hashCode(); } } ``` ``` /** * 人力资源部(Leaf),在组合中表示叶节点对象,叶节点没有子节点 */ public class HRDepartment implements Company { private String name; public HRDepartment(String name) { this.name = name; } /*由于叶子没有再增加分支和树叶,所以add和remove * 方法实现它没有意义,但这样做可以消除叶节点和枝节点 * 对象在抽象层次的区别,它们具备完全一致的接口*/ @Override public void add(Company c) { } @Override public void remove(Company c) { } @Override public void display(int depth) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < depth; i++) { sb.append("-"); } System.out.println(sb.toString() + name); } @Override public void lineOfDuty() { System.out.println(name+" 员工招聘培训管理"); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; HRDepartment that = (HRDepartment) o; return name.equals(that.name); } @Override public int hashCode() { return name.hashCode(); } } ``` ``` /** * 财务部(Leaf),在组合中表示叶节点对象,叶节点没有子节点 */ public class FinanceDepartment implements Company { private String name; public FinanceDepartment(String name) { this.name = name; } /*由于叶子没有再增加分支和树叶,所以add和remove * 方法实现它没有意义,但这样做可以消除叶节点和枝节点 * 对象在抽象层次的区别,它们具备完全一致的接口*/ @Override public void add(Company c) { } @Override public void remove(Company c) { } @Override public void display(int depth) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < depth; i++) { sb.append("-"); } System.out.println(sb.toString() + name); } @Override public void lineOfDuty() { System.out.println(name + " 公司财务收支管理"); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; FinanceDepartment that = (FinanceDepartment) o; return name.equals(that.name); } @Override public int hashCode() { return name.hashCode(); } } ``` ``` /** * 客户端 */ public class MainClass { 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); System.out.println("结构图:"); root.display(1); System.out.println("职责:"); root.lineOfDuty(); } } ``` 输出: ``` 结构图: -北京总公司 ---总公司人力资源部 ---总公司财务部 ---上海华东分公司 -----华东分公司人力资源部 -----华东分公司财务部 -----南京办事处 -------南京办事处人力资源部 -------南京办事处财务部 -----杭州办事处 -------杭州办事处人力资源部 -------杭州办事处财务部 职责: 总公司人力资源部 员工招聘培训管理 总公司财务部 公司财务收支管理 华东分公司人力资源部 员工招聘培训管理 华东分公司财务部 公司财务收支管理 南京办事处人力资源部 员工招聘培训管理 南京办事处财务部 公司财务收支管理 杭州办事处人力资源部 员工招聘培训管理 杭州办事处财务部 公司财务收支管理 ``` 用户只需要选择一个根节点,就可以实现对该节点及其下属的操作。 ## 透明方式与安全方式 - 透明方式,也就是说在Component中声明所有用来管理子对象的方法,其中包括add、remove等。这样实现Component接口的所有子类都具备了add和remove。这样做的好处就是叶节点和枝节点对于外界没有区别,它们具备完全一致的行为接口。但问题也很明显,因为Leaf类本身不具备add()、remove()方法的功能,所以实现它是没有意义的。 - 安全方式,也就是在Component接口中不去声明add和remove方法,那么子类的Leaf也就不需要去实现它,而是在Composite声明所有用来管理子类对象的方法,不过由于不够透明,所以树叶和树枝类将不具有相同的接口,客户端调用需要相应的判断,带来了不便。 ## 何时使用组合模式 - 需求中体现部分与整体层次结构。 - 希望用户可以忽略组合对象与单个对象的不同,统一使用组合结构中的所有对象。 ## 组合模式的好处 组合模式定义了包含基本对象和组合对象的类层次结构。基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去,客户代码中,任何用到基本对象的地方都可以使用组合对象了。用户是不关心到底是处理一个叶节点还是处理一个组合组件,也就用不着为定义组合而写一些选择判断语句了。组合模式让客户可以一致地使用组合结构和单个对象。 Last modification:June 11th, 2020 at 06:23 pm © 允许规范转载