• Java设计模式——迭代器模式


    概述

      网上大部分人说迭代模式的时候,总是以某一种可遍历的对象为例进行介绍。这是可行的,这也是迭代模式的基本原型。当我看到《Head Frist设计模式》中迭代模式的时候,感觉要是能从另一个角度来说明,可能更能够体现迭代模式的威力所在。

      本文介绍的这种迭代模式,倒是更像是适配器-迭代器模式。希望于你有益~


    版权说明

    著作权归作者所有。
    商业转载请联系作者获得授权,非商业转载请注明出处。
    作者:Coding-Naga
    发表日期: 2016年3月4日
    链接:http://blog.csdn.net/lemon_tree12138/article/details/50799562
    来源:CSDN
    更多内容:分类 >> 设计模式


    模式说明

      网上大多数关于迭代器模式的文章,讨论都是建立在一个List或是其他可迭代对象之上的。这样有一个问题,因为都是可迭代的对象,在迭代的过程中我们无法深入了解到迭代器模式能够为我们解决什么样的问题。下面有一个实例,通过这个实例可能可以说明迭代器模式的威力。

    1.实例背景

      有两家餐厅A和B,餐厅A是一家早餐餐厅店,而餐厅B是一家午餐餐厅店。

      现在两个餐厅的大Boss决定将两家餐厅进行合并。在餐厅A中,菜单的逻辑实现是基于ArrayList的,而餐厅B的菜单逻辑实现则是基于数组的。如果去修改任何一家餐厅的菜单实现,都可能会引发一些不必要的修改,且这种修改可能还会导致不必要的Bug,所以A和B都不愿意去修改菜单的原始实现。

      现在的需求是,在不改变两家餐厅对菜单的实现上,再方便地对菜单进行遍历。

                                                                          -- 摘自《Head Frist设计模式》

    2.思路分析

      这个有什么难的?ArrayList和数组都是可循环遍历的对象,那么我们不是可以依次对这两个数组进行循环遍历么?

      不可否认,这是一种最为朴素的实现逻辑,而且易懂。可是,这种方式真的是好的么?如果现在有100家餐厅呢?我们希望的是能够更方便、更优雅地进行遍历菜单,我们希望在后期的项目中,代码的维护性更高,而不是一直“烂”下去。

      我们想如果我们的ArrayList和数组可以有一个公共的接口,那么我们就可以通过这个公共的接口进行迭代,这样一来代码的复用性岂不是更高了么?可是,如何找到这样的一个接口呢?在学习《Effective Java》这本神书的“类和接口”一单时,就了解到我们的程序中,复合优于继承。所以,这里我们可以将ArrayList和数组分别组合到不同的菜单中去,这样对菜单的遍历就是对ArrayList或是数组的遍历。

      因为ArrayList自身就包含了迭代器,所以这里我们不需要为ArrayList创建迭代器。而在数组中则没有这样的迭代逻辑,所以我们要为数组额外创建迭代器。


    3.类图

      根据上面的分析,我们可以画出如下类图:


    图-1 迭代器模式类图

    4.逻辑实现

    (1)公共接口编写

      在类图中的第一个位置就留给了Menu这个接口了,添加此接口的目的在于提高代码的可复用性。这一点在第(4)点迭代逻辑中会有所体现。公共接口代码如下:

    public interface Menu {
        public Iterator<MenuItem> createIterator();
    }

     

    (2)迭代器的创建

      在上面的类图中我们可以找到BreakfastMenu和LunchMenu两个菜单类,它们都是实现了Menu接口的。可是,因为它们包含了不同的容器对象(BreakfastMenu包含了ArrayList,LunchMenu包含了数组),所以在创建迭代器的时候就会有所不同。

      先来看看BreakfastMenu创建迭代器的逻辑代码吧,如下:

    @Override
        public Iterator<MenuItem> createIterator() {
            return menuItems.iterator();
        }
      因为ArrayList自身就包含了迭代器的实现,所以这里就可以直接返回ArrayList的迭代器。而数组没有迭代器的实现部分,所以与上面的创建方式会有所不同,如下:

    @Override
        public Iterator<MenuItem> createIterator() {
            return new LunchIterator(menuItems);
        }

    (3)自定义迭代器

      由于数组本身不具备迭代的功能,所以我们就需要对其进行扩展。可是,如果想要“迭代”数组,其根本实现还是要依赖于数组的循环遍历。因为数组只有这一种方式可以依次提取元素。在迭代器中有两个核心方法:hasNext()和next()。所以,我们就利用这两个方法变相实现对数组的迭代。代码如下:

    public class LunchIterator implements Iterator<MenuItem> {
    
        private MenuItem[] menuItems = null;
        private int position = 0;
        
        public LunchIterator(MenuItem[] _menuItems) {
            this.menuItems = _menuItems;
        }
        
        @Override
        public boolean hasNext() {
            if (menuItems == null) {
                return false;
            }
            
            return position < menuItems.length;
        }
    
        @Override
        public MenuItem next() {
            MenuItem menuItem = menuItems[position];
            position++;
            return menuItem;
        }
    }
      这里有一个额外定义的变量position,它是用来进行元素索引的,我们需要通过此变量提取元素,和判别迭代完成。

     

    (4)迭代逻辑

      这里说的迭代逻辑是针对迭代器之外,客户端的实现逻辑。假定我们有一个女服务员,她可以打印出客户所需要的菜单,而不用关心此菜单的实现方式。

    public class Waitress {
    
        private Iterator<MenuItem> iterator = null;
        private Menu menu = null;
        
        public Waitress(Menu _menu) {
            this.menu = _menu;
        }
        
        public void printMenu() {
            System.out.println("
    菜单:");
            iterator = menu.createIterator();
            do {
                System.out.println(iterator.next());
            } while (iterator.hasNext());
        }
    }

    Ref

    • 《Head Frist设计模式》
    • 《23种Java设计模式》


    Github源码下载

    https://github.com/William-Hai/DesignPatternCollections

  • 相关阅读:
    2014最后一篇英语笔记(新开始)
    记录:CSS特殊性——权值规则
    grunt--自动化打包工具使用
    【移动端】---点透事件
    [前端性能提升]--图片转化为base64
    js--cookie
    1.倒数几秒弹窗关闭
    ES6就是ES2015 的主要内容
    call 与 apply的区别
    34枚金币时间管理法
  • 原文地址:https://www.cnblogs.com/fengju/p/6336008.html
Copyright © 2020-2023  润新知