1+2:组合迭代器

来源:互联网 发布:底盘装甲 知乎 编辑:程序博客网 时间:2024/06/11 03:50

1,前言

前面我们已经了解了两种设计模式

1,迭代器模式 :

封装集合的遍历方式,使不同集合可以使用相同的方式进行遍历

2,组合模式 :

将对象组合成树形结构以表示"部分-整体"的层次结构,可以使用相同的方法处理"分支"和"叶子"节点对象

那么,如果我们需要遍历一颗”树”将如何实现呢? 由以上两种模式创造出”组合迭代器”模式


2,组合迭代器思想

组合迭代器模式:用于遍历组合模式树形结构中的所有组件,在遍历过程中可以对返回的组件对象进行筛选和操作组合迭代器核心思想:递归迭代,即让"分支"节点拥有将子对象集创建为迭代器对象的能力在组合模式中我们将"分支"和"叶子"节点对象继承自相同的组件,从而视分支和叶子节点为相同对象进行处理那显然组合迭代器需要"分支"节点对象继续递归迭代其子对象集,直至"叶子"节点位置,所以这里我们需要拓展组件对象,使"分支"节点可以创建迭代器用于迭代子对象集当然由于分支和叶子节点都继承自该组件,所以在"分支"节点具备创建子对象迭代器功能的同时,"叶子"节点对象也会拥有迭代子对象的功能,但是"叶子"节点下已无子对象,所以在"叶子"节点中我们做一个空的实现即可(我们这里采用一个空的迭代器)

3,组合迭代器

1,在组合模式的基础上,拓展组件对象MenuComponent,使之可以将分支节点对象中包含的对象集创建为迭代器对象
package com.brave.compositeIterator;import java.util.Iterator;/** * 菜单组件(叶子节点,分支节点)的抽象类 * 作用:为 叶子节点(菜单项) 和 分支节点(菜单) 提供统一接口 * 有些方式只对菜单有用,有些只对菜单项有用,这里写上父类默认实现,不重写默认无效抛出异常 * @author Brave * */public abstract class MenuComponent {    /** 增 删 查 **/    public void add(MenuComponent menuComponent){        throw new UnsupportedOperationException();    }    public void remove(MenuComponent menuComponent){        throw new UnsupportedOperationException();    }    public MenuComponent getChild(int i){        throw new UnsupportedOperationException();    }    /** 一些操作 **/    public String getName(){        throw new UnsupportedOperationException();    }    public String getDesc(){        throw new UnsupportedOperationException();    }    public double getPrice(){        throw new UnsupportedOperationException();    }    public boolean isVegetarian() {        throw new UnsupportedOperationException();    }    /** 打印 **/    public void print(int level){        throw new UnsupportedOperationException();    }    /**     * 创建迭代器方法,使分支节点对象可以将子对象集创建为迭代器对象,用于迭代     */    public abstract Iterator createIterator();}
2,在组合模式的基础上,分支节点继承自拓展后的组件对象MenuComponent,菜单(分支)节点需添加对新方法createIterator的实现,这里我们需要返回一个组合迭代器(组合迭代器的核心)
package com.brave.compositeIterator;import java.util.Iterator;import java.util.Stack;/** * 组合迭代器: *  实现了迭代器Iterator * @author Brave * */public class CompositeIterator implements Iterator{    // 堆栈数据结构:后进先出 pop取后删掉,peek只取不删    Stack<Iterator> stack = new Stack<Iterator>();    /**     * 将顶层迭代器对象抛入一个堆栈数据结构中     * @param iterator  顶层迭代器对象     */    public CompositeIterator(Iterator iterator) {        stack.push(iterator);    }    @Override    public boolean hasNext() {        if(stack.empty()){            return false;        }else{            // 如果不为空,从堆顶取出迭代器            Iterator iterator = stack.peek();            //如果没有下一个元素,弹出堆栈            if(!iterator.hasNext()){                stack.pop();// 移除此迭代器对象                return hasNext();//递归检查下一个迭代器            }else{                return true;            }        }    }    @Override    public Object next() {        if(hasNext()){            Iterator iterator = stack.peek();            MenuComponent component = (MenuComponent)iterator.next();            //如果是菜单对象,将本层迭代器放入堆栈,准备读取            if(component instanceof Menu){                stack.push(component.createIterator());            }            return component;        }else{            return null;        }    }}
package com.brave.compositeIterator;import java.util.ArrayList;import java.util.Iterator;/** * 菜单对象 * 菜单(Menu)和菜单项(MenuItem)都拓展自MenuComponent:可以使用相同方式来菜单和菜单项对象 * 对于菜单对象:能够添加,删除,查询菜单项并遍历下级菜单项 * @author Brave * */public class Menu extends MenuComponent{    ArrayList<MenuComponent> menuComponents = new ArrayList<MenuComponent>();    String name;    //名称    String desc;    //描述    public Menu(String name, String desc) {        this.name = name;        this.desc = desc;    }    /** 重写增 删 查 **/    @Override    public void add(MenuComponent menuComponent) {        menuComponents.add(menuComponent);    }    @Override    public void remove(MenuComponent menuComponent){        menuComponents.remove(menuComponent);    }    @Override    public MenuComponent getChild(int i){        return menuComponents.get(i);    }    /** 重写getter **/    @Override    public String getName() {        return name;    }    @Override    public String getDesc() {        return desc;    }    /** 打印 **/    @Override    public void print(int level){        String pre = "";        for(int i=0; i<level; i++){            pre = pre+"--";        }        System.out.println(pre + "*******************************************");        System.out.println(pre + "name = " + getName() + ", Desc = " + getDesc());        System.out.println(pre + "*******************************************");        Iterator<MenuComponent> iterator = menuComponents.iterator();        while(iterator.hasNext()){            MenuComponent menuComponent = iterator.next();            menuComponent.print(level + 2);        }    }    /**     * 将分支节点的子对象集创建为迭代器对象(用于迭代组件)     */    @Override    public Iterator createIterator() {        return new CompositeIterator(menuComponents.iterator());    }}

3,菜单项(叶子)节点也需添加对新方法createIterator的实现,由于菜单项已经是”叶子”节点,不会在有下级节点,也无需迭代,这里我们在createIterator的实现中使用一个空迭代器对象

package com.brave.compositeIterator;import java.util.Iterator;/** * 空迭代器对象 *  因为菜单项已经是叶子节点,下边没有可迭代的对象 * @author Brave * */public class NullIterator implements Iterator {    @Override    public boolean hasNext() {        return false;    }    @Override    public Object next() {        return null;    }}
package com.brave.compositeIterator;import java.util.Iterator;/** * 菜单项对象 * 菜单(Menu)和菜单项(MenuItem)都拓展自MenuComponent:可以使用相同的方式吃力菜单和菜单项对象 * 对于菜单项对象:不能够添加,删除,查询菜单项和菜单项,只能获取菜单项对象的属性 * @author Brave * */public class MenuItem extends MenuComponent{    String name;    //名称    String desc;    //描述    boolean vegetarian; //是否素食    double price;   //价格    public MenuItem(String name, String desc, boolean vegetarian, double price) {        this.name = name;        this.desc = desc;        this.vegetarian = vegetarian;        this.price = price;    }    /** 重写getter **/    @Override    public String getName() {        return name;    }    @Override    public String getDesc() {        return desc;    }    @Override    public double getPrice() {        return price;    }    @Override    public boolean isVegetarian() {        return vegetarian;    }    /** 打印 **/    @Override    public void print(int level){        String pre = "";        for(int i=0; i<level; i++){            pre = pre+"--";        }        System.out.println(pre + "name = " + getName() + ", Desc = "+  getDesc() + ", Price = " + getPrice());    }    /** 返回空迭代器 :因为菜单已经是叶子节点,下边没有可迭代的对象,故返回一个空迭代器对象 **/    @Override    public Iterator createIterator() {        return new NullIterator();    }}

在这个例子中我们对菜单项对象添加了一个新的属性vegetarian,表示菜品是否为素食,后边用于展示组合跌打器的作用

3,和组合模式类似,创建一个树形结构的菜单,但这里我们将菜单的输出封装到一个招待员类,可以输出全部菜单和仅素食菜单

package com.brave.compositeIterator;import java.util.Iterator;/** * 招待员类 * 面向接口编程 MenuComponent, Iterator * 封装了Menu的各种遍历 * @author Brave * */public class Waitress {    MenuComponent allMenus;    public Waitress(MenuComponent allMenus) {        this.allMenus = allMenus;    }    public void printMenu(){        allMenus.print(1);    }    public void printVegetarianMenu() {        // 将菜单创建为一个组合迭代器        Iterator iterator = allMenus.createIterator();        while(iterator.hasNext()){            try{                MenuComponent menuComponent = (MenuComponent)iterator.next();                if(menuComponent.isVegetarian()){                    menuComponent.print(1);                }            }catch(UnsupportedOperationException e){                // 暂不处理 : 菜单对象会进入Catch(因为没有重写isVegetarian)            }        }    }}

通过招待员类对菜单的进一步封装,我们在客户端调用时可以选中输出全部菜单和仅素食菜单

package com.brave.compositeIterator;/** * 组合模式的测试类 * 由于菜单(Menu)和菜单项(MenuItem)都集成自菜单组件(MenuComponent):菜单和菜单项间可以自由的进行组合,并可以使用相同的处理方式 * @author Brave * */public class MentTest {    public static void main(String[] args) {        // 创建早餐菜单        MenuComponent breakFastMenu = new Menu("菜单1","早餐菜单");        breakFastMenu.add(new MenuItem("煎饼果子", "鸡蛋,果子", false, 5.00));        breakFastMenu.add(new MenuItem("豆浆", "豆浆", true, 2.00));        breakFastMenu.add(new MenuItem("豆腐脑", "豆腐脑", true, 3.00));        breakFastMenu.add(new MenuItem("锅巴菜", "锅巴,卤子", true, 4.00));        breakFastMenu.add(new MenuItem("茶叶蛋", "鸡蛋", false, 1.00));        breakFastMenu.add(new MenuItem("云吞", "猪肉,面皮,高汤", false, 4.00));        // 创建晚餐菜单        MenuComponent dinerMenu = new Menu("菜单2","晚餐菜单");        dinerMenu.add(new MenuItem("京酱肉丝", "肉丝,葱,豆皮", false, 25.00));        dinerMenu.add(new MenuItem("五谷丰登", "各种菜", true, 10.00));        dinerMenu.add(new MenuItem("水果沙拉", "各种水果和沙拉", true, 15.00));        dinerMenu.add(new MenuItem("宫保鸡丁", "鸡丁,葱,姜,蒜,微辣", false, 15.00));        // 创建甜点菜单        MenuComponent cakeMenu = new Menu("菜单3","甜点菜单");        cakeMenu.add(new MenuItem("慕斯蛋糕", "黄油,淡奶油,蛋糕", true, 25.00));        cakeMenu.add(new MenuItem("黑森林蛋糕", "巧克力,奶油,樱桃酒,蛋糕", true, 25.00));        cakeMenu.add(new MenuItem("提拉米苏", "芝士,蛋黄,咖啡,可可粉", true, 25.00));        // 组装菜单:甜点菜单添加到晚餐,早餐和晚餐添加到总菜单        dinerMenu.add(cakeMenu);        MenuComponent Menu = new Menu("总菜单","早餐+晚餐");        Menu.add(breakFastMenu);        Menu.add(dinerMenu);        // 展示菜单        Waitress waitress = new Waitress(Menu);        // 全部        waitress.printMenu();        // 仅素食        waitress.printVegetarianMenu();    }}

输出如下:

--*******************************************--name = 总菜单, Desc = 早餐+晚餐--*******************************************------*******************************************------name = 菜单1, Desc = 早餐菜单------*******************************************----------name = 煎饼果子, Desc = 鸡蛋,果子, Price = 5.0----------name = 豆浆, Desc = 豆浆, Price = 2.0----------name = 豆腐脑, Desc = 豆腐脑, Price = 3.0----------name = 锅巴菜, Desc = 锅巴,卤子, Price = 4.0----------name = 茶叶蛋, Desc = 鸡蛋, Price = 1.0----------name = 云吞, Desc = 猪肉,面皮,高汤, Price = 4.0------*******************************************------name = 菜单2, Desc = 晚餐菜单------*******************************************----------name = 京酱肉丝, Desc = 肉丝,葱,豆皮, Price = 25.0----------name = 五谷丰登, Desc = 各种菜, Price = 10.0----------name = 水果沙拉, Desc = 各种水果和沙拉, Price = 15.0----------name = 宫保鸡丁, Desc = 鸡丁,葱,姜,蒜,微辣, Price = 15.0----------*******************************************----------name = 菜单3, Desc = 甜点菜单----------*******************************************--------------name = 慕斯蛋糕, Desc = 黄油,淡奶油,蛋糕, Price = 25.0--------------name = 黑森林蛋糕, Desc = 巧克力,奶油,樱桃酒,蛋糕, Price = 25.0--------------name = 提拉米苏, Desc = 芝士,蛋黄,咖啡,可可粉, Price = 25.0--name = 豆浆, Desc = 豆浆, Price = 2.0--name = 豆腐脑, Desc = 豆腐脑, Price = 3.0--name = 锅巴菜, Desc = 锅巴,卤子, Price = 4.0--name = 五谷丰登, Desc = 各种菜, Price = 10.0--name = 水果沙拉, Desc = 各种水果和沙拉, Price = 15.0--name = 慕斯蛋糕, Desc = 黄油,淡奶油,蛋糕, Price = 25.0--name = 黑森林蛋糕, Desc = 巧克力,奶油,樱桃酒,蛋糕, Price = 25.0--name = 提拉米苏, Desc = 芝士,蛋黄,咖啡,可可粉, Price = 25.0--name = 慕斯蛋糕, Desc = 黄油,淡奶油,蛋糕, Price = 25.0--name = 黑森林蛋糕, Desc = 巧克力,奶油,樱桃酒,蛋糕, Price = 25.0--name = 提拉米苏, Desc = 芝士,蛋黄,咖啡,可可粉, Price = 25.0

3,结论

从以上可以看出,使用组合迭代器模式,可以对树形结构的组合迭代器进行遍历迭代,迭代过程中返回每一个组件对象进行处理其实,对于树形结构的迭代,还可以使用其他的方式这里我们将组合模式和迭代器模式合二为一,也展示了设计模式的强大对于类似java这种面向对象语言,其特性在于继承,多态和封装而设计模式正是对其特性的淋漓尽致的体现设计模式仅仅是一招半式,融会贯通才能发挥真正威力编程,不仅是技术,更是艺术