模式说明
一系列对象可以用树来描述,形成树-子树-叶子的层次机构。如一家大型公司,根节点是总公司,下设若干子公司和若干非公司部门如总公司财经部,总公司人力资源部门。子公司中如同总公司结构,也包含它自己的子公司和非公司部门。则子公司对象是所属公司对象的子树,非公司部门对象是所属公司对象的叶子。客户端对单个对象和组合对象具有一致的访问性。即上层公司A(根)和其子公司B(树枝或子树),其非子公司部门C(树叶)继承同一个抽象构件类,有相同的方法。但因为树叶不再有子节点,根据树叶中是否包含针对子节点的方法(例如add增加儿子,remove,getChild等),分为透明方式和安全方式。
本示例以总公司和子公司场景为例,演示透明方式的组合模式。在客户端中将树枝和树叶机构加入到根(总公司)中,然后调用总公司的方法,实现对其下属机构的递归调用。
透明方式
树叶与树枝具有同样的,在抽象构件类中声明的针对子节点的方法时,对客户端而言,一个对象是树枝还是树叶是透明的,客户端无需关心。这种方式下,树叶无儿子,却仍需实现针对儿子的各种方法(空方法或抛出异常),存在安全性问题。
安全方式
与透明方式相对,不在抽象构件类和树叶实现类中声明针对儿子的方法,只在树枝中实现。缺点是客户端需要提前知道哪些对象是树枝,哪些是树叶,失去透明性。
结构
抽象构件类
树枝和树叶的抽象类,声明了公共抽象方法,实现默认行为。
具体实现树枝类
继承抽象构件类,具有儿子的对象,包含针对儿子的各种方法。
具体实现树叶类
继承抽象构件,不具有儿子的对象。若包含针对儿子的各种方法为透明方式,不包含则为安全模式。
代码演示
package com.yukiyama.pattern.structure;import java.util.ArrayList;import java.util.List;/*** 组合模式*/public class CompositeDemo {public static void main(String[] args) {// 声明上海总公司Company hq = new ConcreteCompany();hq.setName("上海总公司");// 声明上海总公司的叶子机构并添加到总公司中Company hqHR = new HRDepartment();hqHR.setName("上海总公司人力资源部");hq.add(hqHR);Company hqFi = new FinanceDepartment();hqFi.setName("上海总公司财务部");hq.add(hqFi);// 声明上海总公司的树枝机构广州分公司,并完成其下属机构的添加Company gzSub = new ConcreteCompany();gzSub.setName("广州分公司");Company gzHR = new HRDepartment();gzHR.setName("广州分公司人力资源部");gzSub.add(gzHR);Company gzFi = new FinanceDepartment();gzFi.setName("广州分公司财务部");gzSub.add(gzFi);// 将广州分公司添加到上海总公司hq.add(gzSub);// 调用总公司hq的display()方法,递归调用其下分支机构的display()方法System.out.println("====机构====");hq.display();// 调用总公司hq的duty()方法,递归调用其下分支机构的duty()方法System.out.println("====职责====");hq.duty();}}/*** 抽象构件类* 持有实例字段和非抽象实例方法,定义抽象方法。* 下例以公司为抽象构件类。*/abstract class Company{private String name;public void setName(String name) {this.name = name;}public String getName() {return name;}public abstract void add(Company com);public abstract void remove(Company com);public abstract void display();public abstract void duty();}/*** 树枝类* 继承抽象构件类,比抽象类多一个用于保存儿子的List,实现抽象方法。*/class ConcreteCompany extends Company{private List<Company> subs = new ArrayList<>();@Overridepublic void add(Company com) {subs.add(com);}@Overridepublic void remove(Company com) {subs.remove(com);}@Overridepublic void display() {System.out.println(this.getName());for(Company com : subs) {com.display();}}@Overridepublic void duty() {System.out.printf("%s,统筹公司所有事务。\n", this.getName());for(Company com : subs) {com.duty();}}}/*** 树叶类* 继承抽象构件类,实现抽象方法,但对于针对儿子的方法,被调用时打印不支持* 操作的提示。* 下例是HR部门类。*/class HRDepartment extends Company{@Overridepublic void add(Company com) {System.out.printf("%s无子机构,不支持此操作。\n", this.getName());}@Overridepublic void remove(Company com) {System.out.printf("%s无子机构,不支持此操作。\n", this.getName());}@Overridepublic void display() {System.out.println(this.getName());}@Overridepublic void duty() {System.out.printf("%s,负责公司员工招聘薪酬管理。\n", this.getName());}}/*** 树叶类* 下例是财务部门类。*/class FinanceDepartment extends Company{@Overridepublic void add(Company com) {System.out.printf("%s无子机构,不支持此操作。\n", this.getName());}@Overridepublic void remove(Company com) {System.out.printf("%s无子机构,不支持此操作。\n", this.getName());}@Overridepublic void display() {System.out.println(this.getName());}@Overridepublic void duty() {System.out.printf("%s,负责公司财务管理。\n", this.getName());}}
