• 设计模式——迭代器(Iterator)模式


    • 概述

      迭代器模式简单的说(按我目前的理解)就是一个类提供一个对外迭代的接口,方面调用者迭代。这个迭代接口至少包括两个方法:hasNext()--用于判断是否还有下一个,next()--用于取出下一个对象(或值)。而外部使用这个类(取出这个类中的对象或值)时,不用关心这个类存储对象或数据的具体数据结构,即使这个类的存储数据结构临时发生改变,调用者不作任何代码修改仍然可以正常工作。从而实现代码的可重用性和低耦合性。下面以实例说明。

    • 实例

       假设有两个书架(BookShelf),一个书架用数组Array的形式存放书,另一个书架用ArrayList的形式存放书,两个书架可以存放多本书(Book)的名字,Main方法通过书架取出书名打印出来。

      传统模式:很容易想到第一个书架通过一个方法返回存放书的数组,另一个书架返回存放书的ArrayList。就像下面这样:

    //传统模式:
        public Book[] getBooks(){
            return  books;
        }
        //传统模式
        public ArrayList<Book> getBooks(){
            return  books;
        }

      这样的缺点是什么呢?缺点在于当我们同时获取到这两个书架时,迭代输出的方式就有所不同,因为一个是数组,一个是List,这个很容易想到吧,可能你觉得很简单,但是如果有十个,有几十个用不同的数据结构存储呢,迭代的方式又要改。并且,如果我开始用的数组存放书,后来我又不想用数组而改用List存放呢?那岂不是既要修改存放书的代码的同时又要修改迭代输出的代码?Oh,No..太麻烦了吧。可能你会想自己写的代码,干嘛每个书架存放书的数据结构要不同呢?那如果不是自己写的呢?比如我现在要合并多个餐馆的菜单为一个菜单并输出打印,而这些菜单的存储方式肯定不同,很有可能这几个餐馆的代码都不是同一个人写的。这时候我们再去一个一个循环未免也太麻烦。

      使用迭代器模式:既然我不同的书架存放书的数据结构不同,那我可以每个书架对外提供一个迭代接口,而每个接口都包含有hasNext方法和Next方法。这时在外部循环迭代输出时,就不用担心每个书架的书是怎样存放的,我只需要能通过Next方法取出书就可以了,就像下面这样:

    数组存放:

    public class BookShelf {
    
        private Book[] books;
        private int last = 0;
    
        public BookShelf(int maxsize) {
            this.books = new Book[maxsize];
        }
    
        public Book getBookAt(int index){
            return books[index];
        }
    
        public void appendBook(Book book){
            this.books[last] = book;
            last++;
        }
    
        public int getLength(){
            return  last;
        }
    
    //    //传统模式:
    //    public Book[] getBooks(){
    //        return  books;
    //    }
    
        public Iterator iterator() {
    //        return new BookShelfIterator(this);
            return  new BookShelfIterator();
        }
    
        class  BookShelfIterator implements  Iterator{
    //        private BookShelf bookShelf; //采用内部类,可以直接调用外部类方法,不用添加引用。
            private int index ;
            public BookShelfIterator() {
    //            this.bookShelf=bookShelf;
                this.index = 0;
            }
    
            @Override
            public boolean hasNext() {
                if (index < getLength()){
                    return  true;
                }else {
                    return  false;
                }
            }
            @Override
            public Object next() {
                Book book  = getBookAt(index);
                index++;
                return book;
            }
        }
    }

    ArrayList存放:

    import java.util.ArrayList;
    
    public class BookShelf1 {
    
        private ArrayList<Book> books ;//使用ArrayList实现
    
        public BookShelf1(int maxsize) {
            this.books = new ArrayList<>(maxsize);//初始大小
        }
    
        public Book getBookAt(int index){
            return  books.get(index);
        }
    
        public void appendBook(Book book){
            this.books.add(book);
        }
    
        public int getLength(){
            return  books.size();
        }
    
        public Iterator iterator() {
            return  new BookShelfIterator();
        }
    
    //    //普通模式
    ////    public ArrayList<Book> getBooks(){
    ////        return  books;
    ////    }
    
        class  BookShelfIterator implements  Iterator{
            //        private BookShelf bookShelf; //采用内部类,可以直接调用外部类方法,不用添加引用。
            private int index ;
            public BookShelfIterator() {
    //            this.bookShelf=bookShelf;
                this.index = 0;
            }
    
            @Override
            public boolean hasNext() {
                if (index < getLength()){
                    return  true;
                }else {
                    return  false;
                }
            }
            @Override
            public Object next() {
                Book book  = getBookAt(index);
                index++;
                return book;
            }
        }
    
    }

    而我们获取这两个书架的书时,只需要调用两个书架的iterator()方法就可以获取到同样的Iterator对象,这个对象中都包含两个相同的方法hasNext()和Next(),这样我们就很方便的迭代输出了,并且我们不用关心每个书架里面是通过数组还是list存放书的。就像下面这样:

            ArrayList<Iterator> iterList = new ArrayList<>(); //存放Iterator
            BookShelf bookShelf = new BookShelf(4);//实例化第一个书架
            bookShelf.appendBook(new Book("Around the World in 80 Days"));
            bookShelf.appendBook(new Book("Bible"));
            bookShelf.appendBook(new Book("Daddy-Long-Legs"));
            bookShelf.appendBook(new Book("Cinderella"));
            Iterator it  = bookShelf.iterator();
            iterList.add(it);
    
            BookShelf1 bookShelf1 = new BookShelf1(4);//实例化第二个书架
            bookShelf1.appendBook(new Book("Around the World in 80 Days__"));
            bookShelf1.appendBook(new Book("Bible__"));
            bookShelf1.appendBook(new Book("Daddy-Long-Legs__"));
            bookShelf1.appendBook(new Book("Cinderella__"));
            Iterator it1  = bookShelf1.iterator();
            iterList.add(it1);
            for (int i = 0; i < iterList.size(); i++) {
                Iterator iterator = iterList.get(i);
                while (iterator.hasNext()){
                    Book book = (Book) iterator.next();
                    System.out.println(book.getName());
                }
            }
    • 完整代码

      请移步:https://github.com/yyc007/DesignPatterns/

    • 小结

      使用迭代器模式,可以帮助我们编写可以复用的类,当这个类发生改变时,不需要对其它的类进行修改或者很小的修改即可应对。就上面的书架例子来说,不管BookShelf如何变化,只要BookShelf返回的Iterator类的实例没有问题(hasNext方法和Next方法都可以正常工作),即使调用方不对迭代输出的While循环做任何修改都可以正常工作。

  • 相关阅读:
    pdflatex, xelatex, texstudio中文编码问题
    secure erase 时必须umount
    浪潮不能进bios解决过程
    ycsb-命令及参数-与生成的负载类型相关
    zt-Simple source policy routing
    oracle 分页查找数据
    前台调用后台的过程
    初识 java script
    java中的BigDecimal和String的相互转换
    一级缓存、二级缓存、延迟加载、hibernate session 域 pojo三种状态
  • 原文地址:https://www.cnblogs.com/hyyq/p/10116864.html
Copyright © 2020-2023  润新知