组合模式(Composite Pattern)

在项目开发中,有时候我们会遇到将一个对象组合成一个树形结构,来表示“整体-部分”的层次关系,但用户在操作的时候,不需要区分组合对象(树枝节点,包含子节点)或单个对象(叶子节点,不包含子节点),保持一致的操作体验。这时候我们就可以利用组合模式来实现我们的应用。
定义:将对象组合成树形结构以表示“部分-整体”的层次结构, 使得用户对单个对象和组合对象的使用具有一致性。
一、透明实现
1、在组合模式中首先我们要定义一个抽象类,实现树枝节点和叶子节点的公共属性及方法(例如下面代码中的name,position,salary属性及getInfo()方法),并且定义树枝节点,叶子节点的公共抽象方法(例如下例中的:addSubordinete()方法,getAllSubordinates()方法),用来给树枝节点和叶子节点个性实现。

package composite.transparent;
import java.util.ArrayList;
/**
 * 抽象类,定义树枝节点和叶子节点的公共属性,方法
 * 定义抽象方法,供树枝节点和叶子节点实现
 */
public abstract class AbstractCorp {

    private String name;//姓名
    private String position;//职位
    private String salary;//薪水

    //构造方法
    public AbstractCorp(String name,String position,String salary){
        this.name=name;
        this.position=position;
        this.salary=salary;
    }

    //抽象方法 增加一个子节点,可能是树枝节点,也可能是树叶节点
    public abstract void addSubordinate(AbstractCorp corp);

    //抽象方法 返回所有的子节点
    public abstract ArrayList getAllSubordinates();

    //返回信息
    protected void getInfo(){
        System.out.println("姓名:"+this.name+" 职位:"+this.position+" 薪资:"+this.salary);
    }

}

2、然后我们再定义树枝节点类和叶子节点类继承抽象类,使得树枝节点类和叶子节点类不仅继承抽象类的公共方法和属性,还可以个性化的实现抽象类的抽象方法。
树枝节点类:

package composite.transparent;

import java.util.ArrayList;

import composite.transparent.AbstractCorp;
/**
 * 树枝节点类
 */
public class Branch extends AbstractCorp{

    private ArrayList subordinateList=new ArrayList();//子节点集合

    //构造方法,调用父类的构造方法
    public Branch(String name, String position, String salary) {
        super(name, position, salary);
        // TODO Auto-generated constructor stub
    }

    //实现抽象类的方法 增加一个子节点,可能是树枝节点,也可能是树叶节点
    @Override
    public void addSubordinate(AbstractCorp corp) {
        // TODO Auto-generated method stub
        if(this.subordinateList!=null){
            this.subordinateList.add(corp);
        }
    }

    //实现抽象类的方法 返回所有的子节点
    @Override
    public ArrayList getAllSubordinates() {
        // TODO Auto-generated method stub
        return this.subordinateList;
    }

}

叶子节点类:

package composite.transparent;

import java.util.ArrayList;

/**
 * 叶子节点
 */
public class Leaf extends AbstractCorp{

    //构造方法,调用父类的构造方法
    public Leaf(String name, String position, String salary) {
        super(name, position, salary);
        // TODO Auto-generated constructor stub
    }

    /**
     * 实现抽象类的方法,叶子节点不能再添加子节点,所以
     * @Deprecated注解,表示“不建议使用”,
     * 方法空实现抛出一个UnsupportedOperationException异常
     */
    @Deprecated
    @Override
    public void addSubordinate(AbstractCorp corp){
        // TODO Auto-generated method stub
        throw new UnsupportedOperationException();////空实现,直接抛弃一个"不支持请求"异常
    }

    /**
     * 实现抽象类的方法,叶子节点不包含任何子节点,所以
     * @Deprecated注解,表示“不建议使用”,
     * 方法空实现抛出一个UnsupportedOperationException异常
     */
    @Deprecated
    @Override
    public ArrayList getAllSubordinates() {
        // TODO Auto-generated method stub
        throw new UnsupportedOperationException();////空实现,直接抛弃一个"不支持请求"异常
    }

}

场景类:

package composite.transparent;

import java.util.ArrayList;
/**
 * 场景类
 */
public class Client {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        AbstractCorp ceo=new Branch("章建丰","总经理","200w");//总经理
        AbstractCorp techDirector=new Branch("周勇军","技术总监","100w");//技术总监
        techDirector.addSubordinate(new Leaf("张涛","IOS开发工程师","20k"));//ios开发工程师
        techDirector.addSubordinate(new Leaf("魏庭聪","Android开发工程师","20k"));//android开发工程师
        techDirector.addSubordinate(new Leaf("冯适","PHP开发工程师","20k"));//php开发工程师
        AbstractCorp marketDirector=new Branch("宗玺","市场总监","100w");//市场总监
        marketDirector.addSubordinate(new Leaf("张敏","市场营销","30k"));//市场营销
        marketDirector.addSubordinate(new Leaf("史帅","市场营销","30k"));//市场营销
        marketDirector.addSubordinate(new Leaf("王耀","市场营销","30k"));//市场营销
        ceo.addSubordinate(techDirector);
        ceo.addSubordinate(marketDirector);
        print(ceo);//遍历所有节点信息
    }

    /**
     * 打印所有节点信息
     * @param root 根节点
     */
    private static void print(AbstractCorp root){
        ArrayList subordinates=root.getAllSubordinates();
        for(AbstractCorp subordinate:subordinates){
            if(subordinate instanceof Leaf){//叶子节点
                subordinate.getInfo();//打印叶子节点的信息
            }else{
                subordinate.getInfo();//打印树枝节点信息
                print((Branch)subordinate);//递归遍历打印
            }
        }
    }
}

所有对象声明为AbstractCorp类型,用户不需要知道自己调用的是组合对象还是单个对象,保持一致性。

组合模式(Composite Pattern)_第1张图片

如果我们创建一个叶子节点,调用addSubordinate()方法

AbstractCorp leaf=new Leaf("史帅","市场营销","30k");//创建一个叶子节点
        leaf.addSubordinate(new Leaf("王耀","市场营销","30k"));//给叶子节点在添加一个子节点

运行,报错:
这里写图片描述
这是正确的,因为叶子节点不能再添加子节点。但同样这也是不安全的,可能会在运行时出现错误,所以还有另外一种安全的组合模式。
二、安全实现
1、同样首先我们要定义一个抽象类,定义树枝节点和叶子节点的公共属性和方法。

package composite.safe;

import java.util.ArrayList;

/**
 * 抽象类,定义树枝节点和叶子节点的公共属性,方法
 */
public abstract class AbstractCorp {

    private String name;//姓名
    private String position;//职位
    private String salary;//薪水

    //构造方法
    public AbstractCorp(String name,String position,String salary){
        this.name=name;
        this.position=position;
        this.salary=salary;
    }

    //返回信息
    protected void getInfo(){
        System.out.println("姓名:"+this.name+" 职位:"+this.position+" 薪资:"+this.salary);
    }

}

2、树枝节点

package composite.safe;

import java.util.ArrayList;

/**
 * 树枝节点类
 */
public class Branch extends AbstractCorp{

    private ArrayList subordinateList=new ArrayList();//子节点集合

    //构造方法,调用父类的构造方法
    public Branch(String name, String position, String salary) {
        super(name, position, salary);
        // TODO Auto-generated constructor stub
    }

    //增加一个子节点,可能是树枝节点,也可能是树叶节点
    public void addSubordinate(AbstractCorp corp){
        if(this.subordinateList!=null){
            this.subordinateList.add(corp);
        }
    }

    //返回所有的子节点
    public ArrayList getAllSubordinates(){
        return this.subordinateList;
    }

}

继承AbstractCorp类,并且添加了addSubordinate()和getAllSubordinates()方法。
3、叶子节点

package composite.safe;
/**
 * 树叶节点
 */
public class Leaf extends AbstractCorp{

    //构造方法,调用父类的构造方法
    public Leaf(String name, String position, String salary) {
        super(name, position, salary);
        // TODO Auto-generated constructor stub
    }

}

叶子节点没有子节点,所以没有addSubordinate()和getAllSubordinates()方法。没有添加子节点方法,也就不会出现错误了。
4、场景类

package composite.safe;

import java.util.ArrayList;
/**
 * 场景类
 */
public class Client {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Branch ceo=new Branch("章建丰","总经理","200w");//总经理
        Branch techDirector=new Branch("周勇军","技术总监","100w");//技术总监
        techDirector.addSubordinate(new Leaf("张涛","IOS开发工程师","20k"));//ios开发工程师
        techDirector.addSubordinate(new Leaf("魏庭聪","Android开发工程师","20k"));//android开发工程师
        techDirector.addSubordinate(new Leaf("冯适","PHP开发工程师","20k"));//php开发工程师
        Branch marketDirector=new Branch("宗玺","市场总监","100w");//市场总监
        marketDirector.addSubordinate(new Leaf("张敏","市场营销","30k"));//市场营销
        marketDirector.addSubordinate(new Leaf("史帅","市场营销","30k"));//市场营销
        marketDirector.addSubordinate(new Leaf("王耀","市场营销","30k"));//市场营销
        ceo.addSubordinate(techDirector);
        ceo.addSubordinate(marketDirector);
        print(ceo);//遍历所有节点信息
    }

    /**
     * 打印所有节点信息
     * @param root 根节点
     */
    private static void print(Branch root){
        ArrayList subordinates=root.getAllSubordinates();
        for(AbstractCorp subordinate:subordinates){
            if(subordinate instanceof Leaf){//叶子节点
                subordinate.getInfo();//打印叶子节点的信息
            }else{
                subordinate.getInfo();//打印树枝节点信息
                print((Branch)subordinate);//递归遍历树枝节点
            }
        }
    }

}

但是,对象无法统一指定为抽象类型,也就是用户需要区分对象是树枝节点还是叶子节点,这与组合模式的定义相违背。

优点:
● 高层模块调用简单
一棵树形机构中的所有节点都是Component, 局部和整体对调用者来说没有任何区别,也就是说, 高层模块不必关心自己处理的是单个对象还是整个组合结构, 简化了高层模块的代码。
● 节点自由增加
使用了组合模式后, 我们可以看看, 如果想增加一个树枝节点、 树叶节点是不是都很容易, 只要找到它的父节点就成, 非常容易扩展, 符合开闭原则, 对以后的维护非常有利。

项目地址

你可能感兴趣的:(设计模式,组合模式,设计模式之禅)