1+2:组合迭代器

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这种面向对象语言,其特性在于继承,多态和封装
而设计模式正是对其特性的淋漓尽致的体现

设计模式仅仅是一招半式,融会贯通才能发挥真正威力
编程,不仅是技术,更是艺术
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BraveWangDev

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值