组合模式(Composite Pattern)

来源:互联网 发布:linux ld.so.cache 编辑:程序博客网 时间:2024/06/05 15:54

在项目开发中,有时候我们会遇到将一个对象组合成一个树形结构,来表示“整体-部分”的层次关系,但用户在操作的时候,不需要区分组合对象(树枝节点,包含子节点)或单个对象(叶子节点,不包含子节点),保持一致的操作体验。这时候我们就可以利用组合模式来实现我们的应用。
定义:将对象组合成树形结构以表示“部分-整体”的层次结构, 使得用户对单个对象和组合对象的使用具有一致性。
一、透明实现
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<AbstractCorp> 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<AbstractCorp> subordinateList=new ArrayList<AbstractCorp>();//子节点集合    //构造方法,调用父类的构造方法    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<AbstractCorp> 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<AbstractCorp> 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<AbstractCorp> subordinates=root.getAllSubordinates();        for(AbstractCorp subordinate:subordinates){            if(subordinate instanceof Leaf){//叶子节点                subordinate.getInfo();//打印叶子节点的信息            }else{                subordinate.getInfo();//打印树枝节点信息                print((Branch)subordinate);//递归遍历打印            }        }    }}

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

这里写图片描述

如果我们创建一个叶子节点,调用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<AbstractCorp> subordinateList=new ArrayList<AbstractCorp>();//子节点集合    //构造方法,调用父类的构造方法    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<AbstractCorp> 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<AbstractCorp> subordinates=root.getAllSubordinates();        for(AbstractCorp subordinate:subordinates){            if(subordinate instanceof Leaf){//叶子节点                subordinate.getInfo();//打印叶子节点的信息            }else{                subordinate.getInfo();//打印树枝节点信息                print((Branch)subordinate);//递归遍历树枝节点            }        }    }}

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

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

项目地址

原创粉丝点击