迭代器与组合模式

声明 本文内容属于《Head First 设计模式》阅读笔记,文中涉及到的知识案例等直接或间接来源于该书。《Head First 设计模式》通过有趣的图表+文字的形式,让人自然学习设计模式,非常棒推荐阅读


提示 迭代器与组合模式直接看文字表述,理解起来不易,建议阅读代码进行理解。

背景说明

        我们在实际项目中经常会单独用到迭代功能,或单独用到组合功能,这是比较简单的。然而,在某些场景下(如:树状结构等横纵向结合的需求),我们可能需要将迭代与组合结合起来一起使用


迭代器模式、组合模式各自的概念

  • 迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示
  • 组合模式允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合

案例(辅助理解)

情景说明(需求描述)

        现在有一个需求,就是需要有一个菜单组件,这个组件下可以添加其它的菜单,也可以直接添加菜品(即:菜、菜单项)。当然,每个菜单、菜都有属于自己的名字、描述等相关信息;每个菜单需要保证能让调用者直接打印出该菜单下的所有菜品信息;同时也提供迭代能力,使菜单组件能迭代出其下的所有菜单、菜品。

按上述需求,作以下实现

在这里插入图片描述
在这里插入图片描述

上述逻辑相关类

  • MenuComponent:
    import java.util.Iterator;
    
    /**
     * 菜单、菜单项 抽象组件
     *
     * @author JustryDeng
     * @date 2020/3/23 14:00:57
     */
    public interface MenuComponent {
    
        /**
         * 获取名字
         *
         * @return  名字
         */
        default String getName() {
            throw new UnsupportedOperationException("getName");
        }
    
        /**
         * 获取描述
         *
         * @return  描述
         */
        default String getDescription() {
            throw new UnsupportedOperationException("getDescription");
        }
    
        /**
         * 获取价格
         *
         * @return  价格
         */
        default double getPrice(){
            throw new UnsupportedOperationException("getPrice");
        }
    
        /**
         * 是否是蔬菜
         *
         * @return  是否是蔬菜
         */
        default boolean isVegetarian(){
            throw new UnsupportedOperationException("isVegetarian");
        }
    
        /**
         * 添加项
         *
         * @param menuComponent
         *            组件
         */
        default void add(MenuComponent menuComponent){
            throw new UnsupportedOperationException("add");
        }
    
        /**
         * 删除项
         *
         * @param menuComponent
         *            组件
         */
        default void remove(MenuComponent menuComponent){
            throw new UnsupportedOperationException("remove");
        }
    
        /**
         * 获取索引为index的项
         *
         * @param index
         *            索引
         *
         * @return  索引为index的项
         */
        default MenuComponent getChild(int index){
            throw new UnsupportedOperationException("getChild");
        }
    
        /**
         * 打印信息
         */
        default void print(){
            throw new UnsupportedOperationException("print");
        }
    
        /**
         * 创建迭代器
         *
         * @return 迭代器
         */
        default Iterator<MenuComponent> createIterator(){
            throw new UnsupportedOperationException("createIterator");
        }
    
    }
    
  • MenuItemt:
    import com.szlaozicl.designpattern.iteratorcombination.MenuComponent;
    import java.util.Iterator;
    
    /**
     * 具体的菜(单项)
     *
     * 注: 菜单项,可以有:
     *     - 菜名
     *     - 菜描述
     *     - 菜价格
     *     - 是否是蔬菜
     *     - 打印
     *
     * @author JustryDeng
     * @date 2020/3/23 14:36:41
     */
    public class MenuItem implements MenuComponent {
    
        /** 菜名 */
        private String name;
    
        /** 菜描述 */
        private String description;
    
        /** 菜价格 */
        private double price;
    
        /** 是否是蔬菜 */
        private boolean vegetarian;
    
        public MenuItem(String name, String description, double price, boolean vegetarian) {
            this.name = name;
            this.description = description;
            this.price = price;
            this.vegetarian = vegetarian;
        }
    
        @Override
        public String getName() {
            return this.name;
        }
    
        @Override
        public String getDescription() {
            return this.description;
        }
    
        @Override
        public double getPrice() {
            return this.price;
        }
    
        @Override
        public boolean isVegetarian() {
            return this.vegetarian;
        }
    
        /**
         * 打印菜品信息
         */
        @Override
        public void print() {
            String content = " => 【" + name + "】" + (vegetarian ? "是" : "不是") + "蔬菜: "
                    + "\n\t\t其它介绍:" + description
                    + "\n\t\t价格:" + price + "RMB";
            System.out.println(content);
        }
    
        @Override
        public Iterator<MenuComponent> createIterator() {
            return new NullIterator<>();
        }
    
        /**
         * 一个空的迭代对象
         *
         * @author JustryDeng
         * @date 2020/3/23 16:48:12
         */
        public static class NullIterator<T> implements Iterator<T> {
    
            @Override
            public boolean hasNext() {
                return false;
            }
    
            @Override
            public T next() {
                return null;
            }
        }
    }
    
  • Menu:
    import com.szlaozicl.designpattern.iteratorcombination.MenuComponent;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Objects;
    
    /**
     * 菜单
     *
     * 注: 菜单,可以有:
     *     - 菜单名
     *     - 菜单描述
     *     - (针对子菜单组件的功能:)添加菜单组件(添加 子菜单or菜单项)
     *     - (针对子菜单组件的功能:)移除菜单组件(移除 子菜单or菜单项)
     *     - (针对子菜单组件的功能:)获取菜单组件
     *     - 打印
     *
     * @author JustryDeng
     * @date 2020/3/23 14:38:26
     */
    public class Menu implements MenuComponent {
    
        /** 子菜单组件 */
        List<MenuComponent> subMenuComponent;
    
        /** 菜名 */
        private String name;
    
        /** 菜描述 */
        private String description;
    
        public Menu(String name, String description) {
            this(new ArrayList<>(8), name, description);
        }
    
        public Menu(List<MenuComponent> subMenuComponent, String name, String description) {
            this.subMenuComponent = subMenuComponent;
            this.name = name;
            this.description = description;
        }
    
        @Override
        public String getName() {
            return name;
        }
    
        @Override
        public String getDescription() {
            return description;
        }
    
        @Override
        public void add(MenuComponent menuComponent) {
            subMenuComponent.add(menuComponent);
        }
    
        @Override
        public void remove(MenuComponent menuComponent) {
            subMenuComponent.remove(menuComponent);
        }
    
        @Override
        public MenuComponent getChild(int index) {
            return subMenuComponent.get(index);
        }
    
        /**
         * 打印菜品信息
         */
        @Override
        public void print() {
            Objects.requireNonNull(subMenuComponent, "subMenuComponent cannot be null");
            for (MenuComponent menuComponent : subMenuComponent) {
                menuComponent.print();
            }
        }
    
        @Override
        public Iterator<MenuComponent> createIterator() {
            return subMenuComponent.iterator();
        }
    }
    
  • MenuComponentIterator:
    import com.szlaozicl.designpattern.iteratorcombination.impl.Menu;
    
    import java.util.Iterator;
    import java.util.Stack;
    
    /**
     * MenuComponent专用迭代器
     *
     * @author JustryDeng
     * @date 2020/3/23 15:30:55
     */
    public class MenuComponentIterator implements Iterator<MenuComponent> {
    
        Stack<Iterator<MenuComponent>> stack = new Stack<>();
    
        public MenuComponentIterator(Iterator<MenuComponent> iterator) {
            if (iterator != null) {
                this.stack.push(iterator);
            }
        }
    
        @Override
        public boolean hasNext() {
            if (stack.isEmpty()) {
                return false;
            }
            Iterator<MenuComponent> iterator = stack.peek();
            if (iterator.hasNext()) {
                return true;
            } else {
                stack.pop();
                return hasNext();
            }
        }
    
        @Override
        public MenuComponent next() {
            return next(false);
        }
    
        /**
         * 获取下一项
         *
         * @param onlyLeafNode
         *            是否只获取叶节点所代表的元素项
         *            true  - 只获取叶节点所代表的元素项
         *            false - 获取树中的所有节点(叶节点 + 子节点 + 根节点)所代表的元素项
         * @return  菜单组件项
         * @date 2020/3/25 0:06:30
         */
        private MenuComponent next(boolean onlyLeafNode) {
            if (!hasNext()) {
                return null;
            }
            // 到这里,hasNext()必定为true;
            Iterator<MenuComponent> iterator = stack.peek();
            // 根据中hasNext()中的逻辑,这里iterator.next()一定能取得元素
            MenuComponent menuComponent = iterator.next();
            if (menuComponent instanceof Menu) {
                Iterator<MenuComponent> tmpIterator = menuComponent.createIterator();
                stack.push(tmpIterator);
                /*
                 * 如果走 return next();的话,
                 *     那么 => 会遍历所有的MenuItem叶节点(排除根节点 和 中间的Menu子节点)
                 *
                 * 如果不走return next();注释掉的话,
                 *     那么 => 会遍历所有的节点(MenuItem叶节点 + Menu子节点 + Menu根节点)
                 */
                if (onlyLeafNode) {
                    return next();
                }
            }
            return menuComponent;
        }
    }
    

测试一下

  • 测试数据的结构是这样的:
    在这里插入图片描述
  • 测试方法是这样的:
    import com.szlaozicl.designpattern.iteratorcombination.impl.Menu;
    import com.szlaozicl.designpattern.iteratorcombination.impl.MenuItem;
    
    /**
     * 迭代器和组合模式 - 测试
     *
     * @author JustryDeng
    
    
    
     * @date 2020/3/23 17:25:34
     */
    public class Test {
    
        public static void main(String[] args) {
            MenuComponent menuComponent = prepareData();
            /// 测试【MenuComponent打印出所有菜MenuItem】的功能
            menuComponent.print();
    
            System.err.println("\n ------------- JustryDeng的华丽分割线 ------------- \n");
    
            /// 测试【MenuComponentIterator迭代出所有菜单Menu 和 所有菜MenuItem】的功能
            MenuComponentIterator menuComponentIterator = new MenuComponentIterator(menuComponent.createIterator());
            while (menuComponentIterator.hasNext()) {
                MenuComponent mc = menuComponentIterator.next();
                if (mc instanceof Menu) {
                    System.out.println("MenuComponentIterator迭代到的当前项是 => 【菜单】 => " + mc.getName());
                    continue;
                }
                System.out.println("MenuComponentIterator迭代到的当前项是 => 【 菜 】 => " + mc.getName());
            }
        }
    
        /**
         * 组装数据
         *
         * 提示: 组装后的数据结构,见: <image src="迭代器和组合模式 --- 案例测试数据结构图.jpg" />
         *
         * @return  组装后的数据
         */
        private static MenuComponent prepareData() {
            /// => 所有菜单
            // 总菜单
            Menu totalMenu = new Menu("总菜单", "包含了所有菜、菜单");
            // 湘菜菜单
            Menu huNanProvinceMenu = new Menu("湘菜菜单", "包含了所有湖南的菜、菜单");
            // 川菜菜单
            Menu siChuanProvinceMenu = new Menu("川菜菜单", "包含了所有四川的菜、菜单");
            // 绵阳米粉菜单(隶属于川菜菜单)
            Menu mianYangRiceFlourMenu = new Menu("绵阳米粉菜单(隶属于川菜菜单)", "包含了所有绵阳米粉");
    
            /// => 所有菜
            MenuItem menuItemOne = new MenuItem("土豆炖洋芋", "你可能不知道土豆和洋芋的区别~", 8.88, true) ;
            MenuItem menuItemTwo = new MenuItem("西红柿炒番茄", "西红柿和番茄很配噢~", 12.5, true) ;
            MenuItem menuItemThree = new MenuItem("砂锅鳙鱼头", "提高智商、增强记忆、补充营养、延缓衰老~", 29, false) ;
            MenuItem menuItemFour = new MenuItem("油辣莴笋", "香脆美味又下饭~", 16.5, true) ;
            MenuItem menuItemFive = new MenuItem("干菜焖肉", "好吃不油腻~", 22.5, false) ;
            MenuItem menuItemSix = new MenuItem("麻婆豆腐", "麻婆豆腐麻婆一点~", 10, true) ;
            MenuItem menuItemSeven = new MenuItem("水煮鱼", "喜欢吃鱼~", 35, false) ;
            MenuItem menuItemEight = new MenuItem("担担面", "看样子很好吃欸~", 15, true) ;
            MenuItem menuItemNine = new MenuItem("红汤米粉", "红汤更美味~", 8, false) ;
            MenuItem menuItemTen = new MenuItem("清汤米粉", "清汤也不错噢~", 8, false) ;
            MenuItem menuItemEleven = new MenuItem("清红汤米粉", "别纠结了,清红汤吧~", 8, false) ;
    
            /// => 将菜(、菜单)和菜单组装起来
            // 组装总菜单
            totalMenu.add(huNanProvinceMenu);
            totalMenu.add(siChuanProvinceMenu);
            totalMenu.add(menuItemOne);
            totalMenu.add(menuItemTwo);
            // 组装湘菜菜单
            huNanProvinceMenu.add(menuItemThree);
            huNanProvinceMenu.add(menuItemFour);
            huNanProvinceMenu.add(menuItemFive);
            // 组装川菜菜单
            siChuanProvinceMenu.add(mianYangRiceFlourMenu);
            siChuanProvinceMenu.add(menuItemSix);
            siChuanProvinceMenu.add(menuItemSeven);
            siChuanProvinceMenu.add(menuItemEight);
            // 组装绵阳米粉菜单
            mianYangRiceFlourMenu.add(menuItemNine);
            mianYangRiceFlourMenu.add(menuItemTen);
            mianYangRiceFlourMenu.add(menuItemEleven);
            return totalMenu;
        }
    }
    
  • 运行main方法,控制台输出:
    在这里插入图片描述

迭代器与组合模式,学习完毕 !


^_^ 如有不当之处,欢迎指正

^_^ 参考资料
        《Head First 设计模式》
Eric Freeman & Elisabeth Freeman with Kathy Sierra & Bert Bates著,O’Reilly Taiwan公司译,UMLChina改编

^_^ 测试代码托管链接
         https://github.com/JustryDeng…DesignPattern

^_^ 本文已经被收录进《程序员成长笔记(六)》,笔者JustryDeng

发布了160 篇原创文章 · 获赞 741 · 访问量 90万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览