设计模式之禅-组合模式

目录

组合模式

例子

公司管理结构图。

设计模式之禅-组合模式

public 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;
    }
}
public class Branch extends Corp{
    ArrayList<Corp> 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;
    }
}
public class Leaf extends Corp {
    public Leaf(String name, String position, int salary) {
        super(name, position, salary);
    }
}
public class Client {
    public static void main(String[] args) {
        Branch ceo = compositeCorpTree();
        System.out.println(getTreeInfo(ceo));

    }

    public static Branch compositeCorpTree() {
        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);
        root.addSubordinate(k);
        root.addSubordinate(developDep);
        root.addSubordinate(salesDep);
        root.addSubordinate(financeDep);
        developDep.addSubordinate(zhengLaoLiu);
        developDep.addSubordinate(firstDevGroup);
        developDep.addSubordinate(secondDevGroup);
        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) {
        ArrayList<Corp> subordinateList = root.subordinateList;
        StringBuilder info = new StringBuilder();
        for (Corp s : subordinateList) {
            if (s instanceof Leaf) {
                info.append(s.getInfo()).append("\n");
            } else {
                info.append(s.getInfo()).append("\n").append(getTreeInfo((Branch) s));
            }
        }
        return info.toString();
    }
}

定义

将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
设计模式之禅-组合模式

  • Component抽象构件角色
    定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性,比如我们例子中的getInfo就封装到了抽象类中。
  • Leaf叶子构件
    叶子对象,其下再也没有其他的分支,也就是遍历的最小单位。
  • Composite树枝构件
    树枝对象,它的作用是组合树枝节点和叶子节点形成一个树形结构。
public abstract class Component {
    void doSomething(){
        // 业务
    }
}
public class Composite extends Component {
    private final ArrayList<Component> componentArrayList = new ArrayList<>();

    public void add(Component component) {
        this.componentArrayList.add(component);
    }

    public void remove(Component component) {
        this.componentArrayList.remove(component);
    }

    public ArrayList<Component> getChildren() {
        return this.componentArrayList;
    }
}
public class Leaf extends Component{

}
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);
    }

    /**
     * 通过递归遍历树
     * @param root
     */
    public static void display(Composite root) {
        for (Component c : root.getChildren()) {
            if (c instanceof Leaf) {
                //叶子节点
                c.doSomething();
            } else { //树枝节点
                display((Composite) c);
            }
        }
    }
}

优点

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

缺点

直接使用了实现类!这在面向接口编程上是很不恰当的,与依赖倒置原则冲突。

使用场景

  • 维护和展示部分-整体关系的场景,如树形菜单、文件和文件夹管理。
  • 从一个整体中能够独立出部分模块或功能的场景。

注意

只要是树形结构,就要考虑使用组合模式,这个一定要记住,只要是要体现局部和整体的关系的时候,而且这种关系还可能比较深,考虑一下组合模式吧。

扩展

真正的组合模式,一般都是通故数据表形成树形结构,它依靠了关系数据库的非对象存储性能。

透明模式

设计模式之禅-组合模式
透明模式是把用来组合使用的方法放到抽象类中。

public class Leaf extends Component {
    @Override
    @Deprecated
    public void add(Component component) {
        throw new UnsupportedOperationException();
    }

    @Override
    @Deprecated
    public void remove(Component component) {
        throw new UnsupportedOperationException();
    }

    @Override
    @Deprecated
    public ArrayList<Component> getChildren() {
        throw new UnsupportedOperationException();
    }
}
public class Client {
    public static void display(Component root){
        for (Component c : root.getChildren()) {
            if (c instanceof Leaf){
                c.doSomething();
            }else {
                display(c);
            }
        }
    }
}

组合模式的遍历

设计模式之禅-组合模式
添加getParent setParent方法。

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;
    }

    public Corp getParent() {
        return parent;
    }

    public void setParent(Corp parent) {
        this.parent = parent;
    }
}
public class Branch extends Corp{
    private ArrayList<Corp> 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;
    }
}

最佳实践

组合模式在项目中到处都有,比如现在的页面结构一般都是上下结构,上面放系统的 Logo,下边分为两部分:左边是导航菜单,右边是展示区,左边的导航菜单一般都是树形的 结构,比较清晰,有非常多的JavaScript源码实现了类似的树形菜单,大家可以到网上搜索一下。
还有,大家常用的XML结构也是一个树形结构,根节点、元素节点、值元素这些都与我们的组合模式相匹配,之所以本章节不以XML为例子讲解,是因为很少有人还直接读写 XML文件,一般都是用JDOM或者DOM4J了。

上一篇:社区leaf学习笔记|06. 游戏玩家注册、登陆(中)


下一篇:Leaf——美团点评分布式ID生成系统