zoukankan      html  css  js  c++  java
  • 【一起学系列】之迭代器&组合:虽然有点用不上啦

    迭代器模式

    意图

    提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴漏该对象的内部表示

    迭代器模式的诞生

    产品】:嘿,有一个好消息,咱们旗下的餐厅把月巴克的咖啡店吞并了!太棒了!年终奖稳了!

    开发】:Yeah!Yeah!Yeah!

    产品】:但是他们好像反应一个问题,月巴克的点餐系统好像不兼容我们的体系,怎么回事?不就是一个菜单吗?

    开发】:Oh!No!一定它们的 数据结构 不一样导致的,遍历出现了问题!

    产品】:那怎么办?BOSS,你们一起想想办法吧!

    开发】:老大,我们能不能把遍历方法抽取出来啊?我们遍历操作就可以不用考虑各种细节了,只需要管遍历类就好了。

    BOSS】:什么遍历类的,这叫 迭代器 好吗!其实JDK对于迭代器已经维护的很好了,但是咱们这业务也有一点特殊性,就按你说的办吧,办不好的话,刚才说的年终奖就没了。

    开发】:哦,好的(脸上笑嘻嘻,心里MMP)

    HeadFirst 核心代码

    自己整一个迭代器

    定义迭代器持有者 非必须代码

    /**
     * ******************************
     * description:  迭代器持有者
     * ******************************
     */
    public interface MyContainer {
    
        MyIterator getIterator();
    }
    

    迭代器接口

    /**
     * ******************************
     * description:  迭代器接口
     * ******************************
     */
    public interface MyIterator {
    
        boolean hasNext();
    
        Object next();
    }
    

    迭代器工作类 食物菜单

    public class FoodRepository implements MyContainer {
    
        String[] names = {"宫保鸡丁", "麻辣香锅", "油闷大虾"};
    
        @Override
        public MyIterator getIterator() {
            return new NameIterator();
        }
    
        private class NameIterator implements MyIterator {
    
            private int index;
    
            @Override
            public boolean hasNext() {
                return index < names.length;
            }
    
            @Override
            public Object next() {
               return hasNext() ? names[index++] : null;
            }
    
            NameIterator() {
                index = 0;
            }
        }
    }
    

    迭代器工作类 咖啡菜单

    public class CoffeeRepository implements MyContainer {
    
        List<String> names = Arrays.asList("雀巢咖啡", "黑糖玛奇朵", "半点寂寞");
    
        @Override
        public MyIterator getIterator() {
            return new NameIterator();
        }
    
        private class NameIterator implements MyIterator {
    
            private int index;
    
            @Override
            public boolean hasNext() {
                return index < names.size();
            }
    
            @Override
            public Object next() {
               return hasNext() ? names.get(index++) : null;
            }
    
            NameIterator() {
                index = 0;
            }
        }
    }
    

    测试类 观察调用的表现形式

    public class App {
    
        public static void main(String[] args){
            // 餐厅菜单
            FoodRepository food = new FoodRepository();
            MyIterator foodIterator = food.getIterator();
            while (foodIterator.hasNext()) {
                System.out.println("Food: -> " + foodIterator.next());
            }
    
            CodeUtils.spilt();
    
            // 咖啡菜单
            CoffeeRepository coffee = new CoffeeRepository();
            MyIterator coffeeIterator = coffee.getIterator();
            while (coffeeIterator.hasNext()) {
                System.out.println("Coffee: -> " + coffeeIterator.next());
            }
        }
    }
    

    JDK中的迭代

    public class App {
        
        public static void main(String[] args){
            // JDK
            List<String> names = Arrays.asList("Han", "John", "Tomams");
            Iterator<String> iterable = names.iterator();
            while (iterable.hasNext()) {
                System.out.println("JDK Iterator: -> " + iterable.next());
            }
    
            CodeUtils.spilt();
    
            // JDK
            names.forEach(s -> System.out.println("JDK forEach: -> " + s));
        }
    }
    

    因此对于业务上没有什么要求且常见的数据结构,我们不再需要自行定义迭代器

    迭代器模式的设计思路:

    • Iterator 迭代器
    • Concretelterator 具体迭代器
    • Aggregate 集合
    • ConcreteAggregate 具体集合

    简单来说,

    1. 我们需要明确集合的类型(数组,链表,Map,树结构或者普通List)
    2. 我们需要定义迭代器的行为,是否有下一个(遍历完成),取值,移除等等
    3. 遍历的行为或者算法在具体的迭代器中实现,根据不同的数据结构和业务要求完成编码,实现访问一致,但细节不同的效果

    如果看着有点模棱两可,就看完本文后,访问专题设计模式开源项目,里面有具体的代码示例,链接在最下面

    遵循的设计原则

    • 单一职责原则

    说明:迭代器类在设计中仅仅包含集合迭代的作用,它是把原本数据结构中的遍历抽取出来,达到 高内聚 的效果。

    所谓高内聚:当一个模块或一个类被设计成只支持一组相关功能时,我们说它具有 高内聚 的特征。

    什么场景适合使用

    • 访问一个聚合对象的内容而无需暴漏它的内部表示
    • 支持对聚合对象的多种遍历
    • 为遍历不同的聚合结构提供一个统一的接口

    Code/生活中的实际应用

    举一个不是很恰当的例子,我们都用自动贩卖机买过水,付钱之后它会自动滚出来,大家有没有想过它是怎么实现这个效果的呢?它支持瓶装的,罐装的,甚至还支持袋装的,方便面,口红等等五花八门的产品,它的内部结构可能都各不相同,但是最终的表现效果就是我们直接从出口处拿即可,这是不是迭代器模式的一种体现呢?

    迭代器模式UML图

    组合模式

    意图

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

    说人话:想想Java里的File类

    组合模式的误区

    组合模式 不是 一堆模式的组合!

    组合模式的诞生

    开发】:老大,我在写菜单类的时候感觉好痛苦啊!

    BOSS】:怎么了?

    开发】:菜单有真正的菜品,还有父级菜单啊,它们俩得维护两套逻辑,混在一起好难受!

    BOSS】:你在操作文件的时候怎么不觉得难受?你咋不动动脑子想着抽象一下啊!

    开发】:对啊!我去改代码!

    HeadFirst 核心代码

    定义抽象行为

    /**
     * ******************************
     * description:  定义抽象行为
     * ******************************
     */
    public abstract class MenuComponent {
    
        public String name;
    
        /***
         * 添加
         */
        public abstract void add(MenuComponent component) throws Exception;
    
        /***
         * 移除
         */
        public abstract void remove(MenuComponent component) throws Exception;
    
        /***
         * 获取菜单名
         */
        public abstract String getName();
    
        /***
         * 获取子菜单
         */
        public abstract MenuComponent getChild(int i) throws Exception;
    
        /***
         * 打印菜单
         */
        public abstract void print();
    }
    

    实现 “整体”

    public class Menu extends MenuComponent{
    
        List<MenuComponent> menuComponents = new ArrayList<>();
    
        public Menu(String name) {
            this.name = name;
        }
    
        @Override
        public void add(MenuComponent component) {
            this.menuComponents.add(component);
        }
    
        @Override
        public void remove(MenuComponent component) {
            this.menuComponents.remove(component);
        }
    
        @Override
        public String getName() {
            return this.name;
        }
    
        @Override
        public MenuComponent getChild(int i) {
            return menuComponents.get(i);
        }
    
        @Override
        public void print() {
            System.out.println("当前菜单项: " + getName());
            for (MenuComponent component : menuComponents) {
                component.print();
            }
        }
    }
    

    实现 "部分"

    public class MentItem extends MenuComponent{
    
        public MentItem(String name) {
            this.name = name;
        }
    
        @Override
        public void add(MenuComponent component) throws Exception {
            throw new Exception("无法添加");
        }
    
        @Override
        public void remove(MenuComponent component) throws Exception {
            throw new Exception("无法移除");
        }
    
        @Override
        public String getName() {
            return this.name;
        }
    
        @Override
        public MenuComponent getChild(int i) throws Exception {
            throw new Exception("无子节点");
        }
    
        @Override
        public void print() {
            System.out.println("    食物名: " + getName());
        }
    }
    

    测试类

    public class App {
    
        /***
         * 推荐代码阅读顺序:
         *
         * @see MenuComponent
         * @see Menu
         * @see MentItem
         */
        public static void main(String[] args) {
            Menu meat = new Menu("炒菜类");
    
            MentItem item1 = new MentItem("宫保鸡丁");
            MentItem item2 = new MentItem("剁椒鸡蛋");
            MentItem item3 = new MentItem("鱼香肉丝");
    
            Menu vegetable = new Menu("素食");
            MentItem v1 = new MentItem("酸辣土豆丝");
            MentItem v2 = new MentItem("爆炒包菜");
    
            meat.add(item1);
            meat.add(item2);
            meat.add(item3);
    
            vegetable.add(v1);
            vegetable.add(v2);
    
            meat.add(vegetable);
    
            meat.print();
        }
    }
    
    
    /***
     * 输出内容:
     *
     * 当前菜单项: 炒菜类
     *     食物名: 宫保鸡丁
     *     食物名: 剁椒鸡蛋
     *     食物名: 鱼香肉丝
     * 当前菜单项: 素食
     *     食物名: 酸辣土豆丝
     *     食物名: 爆炒包菜
     */
    

    组合模式的设计思路:

    • Component 为组合的对象声明接口或抽象类
    • Leaf 叶子节点(最小单元)
    • Composite 组合节点(即还有子节点的节点)
    • Client 客户端,调用方

    简单来说,

    1. 当我们需要树形结构时,抽象叶子节点和组合节点(有子节点的节点)的共同行为
    2. 让两者实现同一个接口

    如果看着有点模棱两可,就看完本文后,访问专题设计模式开源项目,里面有具体的代码示例,链接在最下面

    什么场景适合使用

    • 需要表示对象的部分-整体层次结构
    • 希望用户忽略组合对象与单个对象的不同,用户统一地使用组合结构中所有对象

    Code/生活中的实际应用

    依然是一个不太恰当的例子,我们在操作文件和文件夹的时候,都有其移动,复制,重命名,查看文件大小等等功能,对于Java来说,它的底层实现是有一个 是否是文件夹的方法来区分,但实际上这也是组合模式的根本思想,即对于表示 部分 的对象,和 整体 的对象,拥有统一的操作行为

    组合模式的UML图

    总结

    • 迭代器模式:该模式在JDK中已经封装的非常好,我们其实不太需要再自行处理,不过在处理特殊数据结构时这种统一操作的思想仍然值得借鉴
    • 组合模式:组合模式仅在需要树形结构的场景下可发挥巨大的作用,同样的,它规范不同类型对象的行为,统一操作的思想,值得我们借鉴

    相关代码链接

    GitHub地址

    • 兼顾了《HeadFirst》以及《GOF》两本经典书籍中的案例
    • 提供了友好的阅读指导

  • 相关阅读:
    AnyChart图表控件使用指南(四)
    AnyChart图表控件使用指南(五)
    路由器相关
    PHP 实现导出CSV格式文件
    Node.js与Golang使用感受与小结【一】
    Node.js与Golang使用感受与小结【三】JS异步流程控制(序列模式、并发模式、有限并发模式)
    Node.js与Golang使用感受与小结【二】
    js BOM操作
    新建 indexedDB 数据库并插入数据
    vue2项目中引用外部js文件
  • 原文地址:https://www.cnblogs.com/kkzhilu/p/13264162.html
Copyright © 2011-2022 走看看