将对象组合成树形结构,以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
一、公司组织架构1(接口篇)
1. 公司人员接口
public interface ICorp {
//获取自己的信息(每个员工都有信息,你想隐藏,门儿都没有)
public String getInfo();
}
2. 树叶接口
public interface ILeaf extends ICorp{
}
3. 树枝接口(包括大老板节点 和 经理节点 )
public interface IBranch extends ICorp{
//能够增加小兵(树叶节点)或者是经理(树枝节点)—— 不管传什么都是向上造型
public void addSubordinate(ICorp corp);
//我还要能够获得下属的信息
public ArrayList<ICorp> getSubordinate();
}
4. 树叶实现类
public class Leaf implements ILeaf{
//叶子叫什么名字
private String name = "";
//叶子的职位
private String position = "";
//叶子的薪水
private int salary = 0;
//通过构造函数传递信息
public Leaf(String name, String position, int salary) {
this.name = name;
this.position = position;
this.salary = salary;
}
//最小的小兵只能获得自己的信息了
public String getInfo() {
String info = "";
info = "名称:"+this.name;
info = info + "\t职位:"+this.position;
info = info + "\t薪水:"+this.salary;
return info;
}
}
5. 树枝实现类
public class Branch implements IBranch{
//存储子节点的信息
private ArrayList subordinateList = new ArrayList();
//树枝节点的名称
private String name = "";
//树枝节点的职位
private String position = "";
//树枝节点的薪水
private int salary = 0;
//通过构造函数传递树枝节点的参数
public Branch(String name, String position,int salary) {
this.name = name;
this.position = position;
this.salary = salary;
}
//增加一个下属,可能 是小头目,也可能是个小兵
public void addSubordinate(ICorp corp) {
this.subordinateList.add(corp);
}
//我有哪些下属
public ArrayList<ICorp> getSubordinate() {
return this.subordinateList;
}
//得到自己的信息
public String getInfo() {
String info = "";
info = "名称:"+this.name;
info = info + "\t职位:"+this.position;
info = info + "\t薪水:"+this.salary;
return info;
}
}
6. 场景类
public class Client {
public static void main(String[] args) {
//首先是组装一个组织结构出来
Branch ceo = compositeCorpTree();
//首先把ceo的信息打印出来
System.out.println(ceo.getInfo());
//然后是所有员工信息
System.out.println(getTreeInfo(ceo));
}
public static Branch compositeCorpTree(){
//首先产生总经理CEO
Branch root = new Branch("王大麻子", "总经理", 100000);
//把三个部门经理产生出来
Branch developDep = new Branch("刘大瘸子", "研发部门经理", 10000);
Branch salesDep = new Branch("马二拐子", "销售部门经理", 20000);
Branch financeDep = new Branch("赵三驼子", "财务部门经理", 30000);
//再把三个小组长产生出来
Branch firstDevGroup = new Branch("杨三也斜","开发一组组长",5000);
Branch secondDevGroup = new Branch("吴大植株","开发二组组长",6000);
//把所有的小兵都产生出来
Leaf a = new Leaf("a","开发人员",2000);
Leaf b = new Leaf("b","开发人员",2000);
Leaf c = new Leaf("c","开发人员",2000);
Leaf d = new Leaf("d","开发人员",2000);
Leaf e = new Leaf("e","开发人员",2000);
Leaf f = new Leaf("f","开发人员",2000);
Leaf g = new Leaf("g","开发人员",2000);
Leaf h = new Leaf("h", "销售人员", 5000);
Leaf i = new Leaf("i", "销售人员", 4000);
Leaf j = new Leaf("j", "财务人员", 5000);
Leaf k = new Leaf("k", "CEO秘书", 8000);
Leaf zhengLaoLiu = new Leaf("郑老六", "研发部副经理", 20000);
//开始组装
//CEO下有三个部门经理和一个秘书
root.addSubordinate(developDep);
root.addSubordinate(salesDep);
root.addSubordinate(financeDep);
root.addSubordinate(k);
//研发部经理
developDep.addSubordinate(firstDevGroup);
developDep.addSubordinate(secondDevGroup);
developDep.addSubordinate(zhengLaoLiu);
//看看两个开发小组下有什么
firstDevGroup.addSubordinate(a);
firstDevGroup.addSubordinate(b);
firstDevGroup.addSubordinate(c);
secondDevGroup.addSubordinate(d);
secondDevGroup.addSubordinate(e);
secondDevGroup.addSubordinate(f);
//再看销售部下的人员情况
salesDep.addSubordinate(h);
salesDep.addSubordinate(i);
//最后一个财务
financeDep.addSubordinate(j);
return root;
}
//遍历整棵树,只要给我根节点,我就能遍历出所有的节点
public static String getTreeInfo(Branch root){
List<ICorp> subordinateList = root.getSubordinate();
String info = "";
for(ICorp s : subordinateList){
if(s instanceof Leaf){
info = info + s.getInfo() + "\n";
}else{ //是个小头目
info = info + s.getInfo() + "\n" + getTreeInfo((Branch)s);
}
}
return info;
}
}
二、公司组织架构(抽象类篇)
主要是把ICorp接口修改为Corp抽象类,抽象出来很多公共地方,方便很多。
1. 抽象公司职员类
public abstract class Corp {
//公司每个人都有名称
private String name = "";
//公司每个人都有职位
private String position = "";
//公司每个人都有薪水
private int salary = 0;
public Corp(String name, String position, int salary) {
this.name = name;
this.position = position;
this.salary = salary;
}
//获取员工信息
public String getInfo(){
String info = "";
info = "名称:"+this.name;
info = info + "\t职位:"+this.position;
info = info + "\t薪水:"+this.salary;
return info;
}
}
2. 树叶节点
public class Leaf extends Corp{
//就写一个构造函数,这个是必须 的
public Leaf(String name, String position, int salary) {
super(name, position, salary);
}
}
3. 树枝节点
public class Branch extends Corp{
//存储子节点的信息
private ArrayList subordinateList = new ArrayList();
//构造函数是必须 的
public Branch(String name, String position, int salary) {
super(name, position, salary);
}
//增加一个下属,可能 是小头目,也可能是个小兵
public void addSubordinate(Corp corp) {
this.subordinateList.add(corp);
}
//我有哪些下属
public ArrayList<Corp> getSubordinate() {
return this.subordinateList;
}
}
4. 稍稍修改场景类
代码太长,同上例,修改其中部分方法
//遍历整棵树,只要给我根节点,我就能遍历出所有的节点
public static String getTreeInfo(Branch root){
List<Corp> subordinateList = root.getSubordinate();
String info = "";
for(Corp s : subordinateList){
if(s instanceof Leaf){
info = info + s.getInfo() + "\n";
}else{ //是个小头目
info = info + s.getInfo() + "\n" + getTreeInfo((Branch)s);
}
}
return info;
}
三、组合模式定义

Leaf叶子构件:叶子对象,其下再也没有其它分支,也就是遍历的最小单位。
Composite树枝构件:树枝对象,作用是组合树枝节点和叶子节点形成一个树形结构。
1. 抽象构件
public abstract class Component {
//个体和整体都具有的共享
public void doSomething(){
//编写业务逻辑
}
}
2. 树枝构件
public class Composite extends Component{
//构件容器
private List<Component> componentArrayList = new ArrayList<Component>();
//增加一个叶子构件或树枝构件
public void add(Component component){
this.componentArrayList.add(component);
}
//删除一个叶子构件或树枝构件
public void remove(Component component){
this.componentArrayList.remove(component);
}
//获得分支下的所有叶子构件和树枝构件
public List<Component> getChildren(){
return this.componentArrayList;
}
}
3. 树叶构件
public class Leaf extends Component{
/*
* 可以覆写父类方法
* public void doSomething(){
*
* }
*/
}
4. 场景类
public class Client {
public static void main(String[] args) {
//创建一个根节点
Composite root = new Composite();
root.doSomething();
//创建一个树枝构件
Composite branch = new Composite();
//创建一个叶子节点
Leaf leaf = new Leaf();
//建立整体
root.add(branch);
branch.add(leaf);
}
//通过递归遍历树
public static void display(Composite root){
for(Component c:root.getChildren()){ //叶子节点
if(c instanceof Leaf){
c.doSomething();
}else{ //树枝节点
display((Composite)c);
}
}
}
}
四、组合模式的应用
1.优点:
高层模块调用简单:一棵树形机构中的所有节点都是Component,局部和整体对调用者来说没有任何区别,也就是说,高层模块不必关心自己处理的是单个对象还是整个组合结构,简化了高层模块的代码。
节点自由增加:使用组合模式后,如果想增加一个树枝节点、树叶节点都很容易,只要找到它的父节点就成,符合开闭原则。
2. 缺点
看到在我们的场景类中定义,提到树叶和树枝使用时的定义了吗?直接使用了实现类!这在面向接口编程上是很不恰当的,与依赖倒置原则冲突,读者在使用时要考虑清楚,它限制了你接口的影响范围
3. 使用用场景
1)维护和展示部分-整体关系的场景,如树形菜单、文件和文件夹管理。
2)从一个整体中能够独立出部分模块或功能的场景。
4. 组合模式的注意事项
只要是树形结构,就要考虑使用组合模式,这个一定要记住,只要是要体现局部和整体的关系的时候,而且这种关系还可能比较深,考虑一下组合模式吧
五、组合模式的扩展
1. 真实的组合模式
上述Client程序没没有改变多少,树的组装是跑不了的,但树的组装并不是像例子中的new,而是一般先有一张数据库的表,这张数据表定义了一个树形结构,我们把它读取出来,然后展现到前台上,用for循环加上递归就可以完成这个读取,
我们上面的例子是安全模式,下面将下透明模式,透明模式是把用来组合使用的方法放到抽象中,比如add()、remove()以及getChildren等方法(),不管叶子对象不是树枝对象都有相同的结构,通过判断是getChildren的返回值确认是叶子节点 不是树枝节点,如果处理不当会在运行期出现错误,所以建议用上述例子中的安全模式。
1) 抽象构件
public abstract class Component {
//个体和整体都具有的共享
public void doSomething(){
//编写业务逻辑
}
//增加一个叶子构件或树枝构件
public abstract void add(Component component);
//删除一个叶子构件或树枝构件
public abstract void remove(Component component);
//获得分支下的所有叶子构件和树枝构件
public abstract ArrayList<Component> getChildren();
}
2) 树叶节点
public class Leaf extends Component{
public void add(Component component) {
//空实现,直接抛一个“不支持请求”异常
throw new UnsupportedOperationException();
}
public void remove(Component component) {
//空实现,直接抛一个“不支持请求”异常
throw new UnsupportedOperationException();
}
public ArrayList<Component> getChildren() {
//空实现,直接抛一个“不支持请求”异常
throw new UnsupportedOperationException();
}
}
3) 树结构遍历
public class Client {
public static void main(String[] args) {
}
//通过递归遍历树
public static void display(Component root){
for(Component c:root.getChildren()){
if(c instanceof Leaf){
c.doSomething();
}else{ //树枝节点
display(c);
}
}
}
}
3. 组合模式的遍历(获取父节点)
上面例子是从上向下获取子节点,如果我想获取父节点,可以这样做
1)抽象构件
public abstract class Corp {
//公司每个人都有名称
private String name = "";
//公司每个人都有职位
private String position = "";
//公司每个人都有薪水
private int salary = 0;
//父节点是谁
private Corp parent = null;
public Corp(String name, String position, int salary) {
this.name = name;
this.position = position;
this.salary = salary;
}
//获取员工信息
public String getInfo(){
String info = "";
info = "名称:"+this.name;
info = info + "\t职位:"+this.position;
info = info + "\t薪水:"+this.salary;
return info;
}
//设置父节点
protected void setParent(Corp _parent){
this.parent = _parent;
}
//得到父节点
public Corp getParent(){
return this.parent;
}
}
2)树枝构件
public class Branch extends Corp{
//存储子节点的信息
private ArrayList subordinateList = new ArrayList();
//构造函数是必须 的
public Branch(String name, String position, int salary) {
super(name, position, salary);
}
//增加一个下属,可能 是小头目,也可能是个小兵
public void addSubordinate(Corp corp) {
corp.setParent(this); //设置父节点
this.subordinateList.add(corp);
}
//我有哪些下属
public ArrayList<Corp> getSubordinate() {
return this.subordinateList;
}
}
六、最佳实践
一般主页左边的导航菜单或者有树形结构的模块都可以考虑。