美文网首页设计模式
设计模式之禅-组合模式

设计模式之禅-组合模式

作者: 凉快先生 | 来源:发表于2020-12-28 15:14 被阅读0次

    公司的人事管理就是一个典型的树状结构,老大,往下一层一层的管理,最后到我们这层小兵,很典型的树状结构

    今天的任务就是要把这个树状结构实现出来,并且还要把它遍历一遍。

    从这个树状结构上分析,有两种节点:有分支的节点(如研发部经理)和无分支的节点(如员工 A、员工 D 等),我们增加一点学术术语上去,总经理叫做根节点(是不是想到 XML 中的那个根节点 root,那就对了),类似研发部经理有分支的节点叫做树枝节点,类似员工 A 的无分支的节点叫做树叶节点。

    定义一个根节点,就为总经理服务

    public interface IRoot {

        //得到总经理的信息

        public String getInfo();

        //总经理下边要有小兵,那要能增加小兵,比如研发部总经理,这是个树枝节点

        public void add(IBranch branch);

        //那要能增加树叶节点

        public void add(ILeaf leaf);

        //既然能增加,那要还要能够遍历,不可能总经理不知道他手下有哪些人

        public ArrayList getSubordinateInfo();

    }

    根节点的实现类

    public class Root implements IRoot {

        //保存根节点下的树枝节点和树叶节点,Subordinate的意思是下级

        private ArrayList subordinateList = new ArrayList();

        //根节点的名称

        private String name = "";

        //根节点的职位

        private String position = "";

        //根节点的薪水

        private int salary = 0;

        //通过构造函数传递进来总经理的信息

        public Root(String name,String position,int salary){

            this.name = name;

            this.position = position;

            this.salary = salary;

        }

        //增加树枝节点

        public void add(IBranch branch) {

            this.subordinateList.add(branch);

        }

        //增加叶子节点,比如秘书,直接隶属于总经理

        public void add(ILeaf leaf) {

            this.subordinateList.add(leaf);

        }

        //得到自己的信息

        public String getInfo() {

            String info = "";

            info = "名称:"+ this.name;;

            info = info + "\t职位:" + this.position;

            info = info + "\t薪水: " + this.salary;

            return info;

        }

        //得到下级的信息

        public ArrayList getSubordinateInfo() {

            return this.subordinateList;

        }

    }

    很简单,通过构造函数传入参数,然后获得信息,还可以增加子树枝节点(部门经理)和叶子节点(秘书)

    树枝节点,也就是各个部门经理和组长的角色

    public interface IBranch {

        //获得信息

        public String getInfo();

        //增加数据节点,例如研发部下的研发一组

        public void add(IBranch branch);

        //增加叶子节点

        public void add(ILeaf leaf);

        //获得下级信息

        public ArrayList getSubordinateInfo();

    }

    所有的树枝节点

    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 add(IBranch branch) {

            this.subordinateList.add(branch);

        }

        //增加一个叶子节点

        public void add(ILeaf leaf) {

            this.subordinateList.add(leaf);

        }

        //获得自己树枝节点的信息

        public String getInfo() {

            String info = "";

            info = "名称:" + this.name;

            info = info + "\t职位:"+ this.position;

            info = info + "\t薪水:"+this.salary;

            return info;

        }

        //获得下级的信息

        public ArrayList getSubordinateInfo() {

            return this.subordinateList;

        }

    }

    叶子节点,也就是最小的小兵了,只能自己干活,不能指派别人了

    public interface ILeaf {

        //获得自己的信息呀

        public String getInfo();

    }

    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;

        }

    }

    好了,所有的根节点,树枝节点和叶子节点都已经实现了,从总经理、部门经理到最终的员工都已经实现了,然后的工作就是组装成一个树状结构和遍历这个树状结构

    public class Client {

        public static void main(String[] args) {

            //首先产生了一个根节点

            IRoot ceo = new Root("王大麻子","总经理",100000);

            //产生三个部门经理,也就是树枝节点

            IBranch developDep = new Branch("刘大瘸子","研发部门经理",10000);

            IBranch salesDep = new Branch("马二拐子","销售部门经理",20000);

            IBranch financeDep = new Branch("赵三驼子","财务部经理",30000);

            //再把三个小组长产生出来

            IBranch firstDevGroup = new Branch("杨三乜斜","开发一组组长",5000);

            IBranch secondDevGroup = new Branch("吴大棒槌","开发二组组长",6000);

            //剩下的及时我们这些小兵了,就是路人甲,路人乙

            ILeaf a = new Leaf("a","开发人员",2000);

            ILeaf b = new Leaf("b","开发人员",2000);

            ILeaf c = new Leaf("c","开发人员",2000);

            ILeaf d = new Leaf("d","开发人员",2000);

            ILeaf e = new Leaf("e","开发人员",2000);

            ILeaf f = new Leaf("f","开发人员",2000);

            ILeaf g = new Leaf("g","开发人员",2000);

            ILeaf h = new Leaf("h","销售人员",5000);

            ILeaf i = new Leaf("i","销售人员",4000);

            ILeaf j = new Leaf("j","财务人员",5000);

            ILeaf k = new Leaf("k","CEO秘书",8000);

            ILeaf zhengLaoLiu = new Leaf("郑老六","研发部副总",20000);

            //该产生的人都产生出来了,然后我们怎么组装这棵树

            //首先是定义总经理下有三个部门经理

            ceo.add(developDep);

            ceo.add(salesDep);

            ceo.add(financeDep);

            //总经理下还有一个秘书

            ceo.add(k);

            //定义研发部门 下的结构

            developDep.add(firstDevGroup);

            developDep.add(secondDevGroup);

            //研发部经理下还有一个副总

            developDep.add(zhengLaoLiu);

            //看看开发两个开发小组下有什么

            firstDevGroup.add(a);

            firstDevGroup.add(b);

            firstDevGroup.add(c);

            secondDevGroup.add(d);

            secondDevGroup.add(e);

            secondDevGroup.add(f);

            //再看销售部下的人员情况

            salesDep.add(h);

            salesDep.add(i);

            //最后一个财务

            financeDep.add(j);

            //树状结构写完毕,然后我们打印出来

            System.out.println(ceo.getInfo());

            //打印出来整个树形

            getAllSubordinateInfo(ceo.getSubordinateInfo());

        }

        //遍历所有的树枝节点,打印出信息

        private static void getAllSubordinateInfo(ArrayList subordinateList){

            int length = subordinateList.size();

            for(int m=0;m<length;m++){ //定义一个ArrayList长度,不要在for循环中每次计算

                Object s = subordinateList.get(m);

                if(s instanceof Leaf){ //是个叶子节点,也就是员工

                    ILeaf employee = (ILeaf)s;

                    System.out.println(((Leaf) s).getInfo());

                }else{

                    IBranch branch = (IBranch)s;

                    System.out.println(branch.getInfo());

                    //再递归调用

                    getAllSubordinateInfo(branch.getSubordinateInfo());

                }

            }

        }

    }

    如果是在我们的项目中有这样的程序,肯定是被拉出来做典型的。

    getInfo 每个接口都有为什么不能抽象出来?Root 类和 Branch 类有什么差别?为什么要定义成两个接口两个类?如果我要加一个任职期限,你是不是每个类都需要修改?如果我要后序遍历(从员工找到他的上级领导)能做吗?

    问题很多,我们一个一个解决,先说抽象的问题,确实可以吧 IBranch 和 IRoot 合并成一个接口,这个我们先肯定下来,这是个比较大的改动,我们先画个类图:

    这个类图还是有点问题的,接口的作用是什么?定义共性,那 ILeaf 和 IBranch 是不是也有共性呢?有 getInfo(),我们是不是要把这个共性也已经封装起来呢?

    类图上有两个接口,ICorp 是公司所有人员的信息的接口类,不管你是经理还是员工,你都有名字,职位,薪水,这个定义成一个接口没有错,IBranch 有没有必要呢?我们先实现出来然后再说。

    公司类,定义每个员工都有信息

    public interface ICorp {

        //每个员工都有信息

        public String getInfo();

    }

    Leaf是树叶节点,在这里就是我们这些小兵

    public class Leaf implements ICorp {

        //小兵也有名称

        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;

        }

    }

    些经理和小组长是怎么实现的,先看接口:

    public interface IBranch { 

        //能够增加小兵(树叶节点)或者是经理(树枝节点)

        public void addSubordinate(ICorp corp);

        //我还要能够获得下属的信息

        public ArrayList<ICorp> getSubordinate();

    }

    实现类:

    public class Branch implements IBranch, ICorp {

        //领导也是人,也有名字

        private String name = "";

        //领导和领导不同,也是职位区别

        private String position = "";

        //领导也是拿薪水的

        private int salary = 0;

        //领导下边有那些下级领导和小兵

        ArrayList<ICorp> subordinateList = new ArrayList<ICorp>();

        //通过构造函数传递领导的信息

        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;

        }

    }

    组装这个树形结构,并展示出来

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

        }

    }

    你看 Leaf和 Branch中都有 getInfo 信息,是否可以抽象,好,我们抽象一下:

    定义一个公司的人员的抽象类

    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;

        }

    }

    普通员工很简单,就写一个构造函数就可以了

    public class Leaf extends Corp {

        //就写一个构造函数,这个是必须的

        public Leaf(String _name,String _position,int _salary){

            super(_name,_position,_salary);

        }

    }

    小头目的实现类:

    public class Branch extends Corp {

        //领导下边有那些下级领导和小兵

        ArrayList<Corp> subordinateList = new ArrayList<Corp>();

        //构造函数是必须的了

        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 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(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.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;

        }

    }

    组合模式通用类图如下:

    我们先来说说组合模式的几个角色:

    抽象构件角色(Component):定义参加组合的对象的共有方法和属性,可以定义一些默认的行为或属性;比如我们例子中的 getInfo 就封装到了抽象类中。

    叶子构件(Leaf):叶子对象,其下再也没有其他的分支。

    树枝构件(Composite):树枝对象,它的作用是组合树枝节点和叶子节点;

    组合模式有两种模式,透明模式和安全模式,这两个模式有什么区别呢?先看类图:

    从类图上大家应该能看清楚了,这两种模式各有优缺点,透明模式是把用来组合使用的方法放到抽象类中,比如 add(),remove()以及 getChildren 等方法(顺便说一下,getChildren 一般返回的结果为 Iterable的实现类,很多,大家可以看 JDK 的帮助),不管叶子对象还是树枝对象都有相同的结构,通过判断是getChildren 的返回值确认是叶子节点还是树枝节点,如果处理不当,这个会在运行期出现问题的,不是很建议的方式;安全模式就不同了,它是把树枝节点和树叶节点彻底分开,树枝节点单独拥有用来组合的方法,这种方法比较安全,我们的例子使用了安全模式。

    第一个优点只要是树形结构,就要考虑使用组合模式。

    我们在上面也还提到了一个问题,就是树的遍历问题,从上到下遍历没有问题,但是我要是从下往上遍历呢?

    定义一个公司的人员的抽象类

    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;

        }

    }

    节点类:

    public class Branch extends Corp {

        //领导下边有那些下级领导和小兵

        ArrayList<Corp> subordinateList = new ArrayList<Corp>();

        //构造函数是必须的了

        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;

        }

    }

    相关文章

      网友评论

        本文标题:设计模式之禅-组合模式

        本文链接:https://www.haomeiwen.com/subject/xmuznktx.html