zoukankan      html  css  js  c++  java
  • 迭代器模式

    1.定义

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

    把遍历的功能封装到迭代器中,让数据结构管理数据,遍历的功能交给迭代器.

    2.代码实现

    jdk的util包中包含了迭代器接口,代码如下:

    public interface Iterator<E> {
        /**
         * Returns {@code true} if the iteration has more elements.
         * (In other words, returns {@code true} if {@link #next} would
         * return an element rather than throwing an exception.)
         *
         * @return {@code true} if the iteration has more elements
         */
        boolean hasNext();
    
        /**
         * Returns the next element in the iteration.
         *
         * @return the next element in the iteration
         * @throws NoSuchElementException if the iteration has no more elements
         */
        E next();
    
        /**
         * Removes from the underlying collection the last element returned
         * by this iterator (optional operation).  This method can be called
         * only once per call to {@link #next}.  The behavior of an iterator
         * is unspecified if the underlying collection is modified while the
         * iteration is in progress in any way other than by calling this
         * method.
         *
         * @implSpec
         * The default implementation throws an instance of
         * {@link UnsupportedOperationException} and performs no other action.
         *
         * @throws UnsupportedOperationException if the {@code remove}
         *         operation is not supported by this iterator
         *
         * @throws IllegalStateException if the {@code next} method has not
         *         yet been called, or the {@code remove} method has already
         *         been called after the last call to the {@code next}
         *         method
         */
        default void remove() {
            throw new UnsupportedOperationException("remove");
        }
    
        /**
         * Performs the given action for each remaining element until all elements
         * have been processed or the action throws an exception.  Actions are
         * performed in the order of iteration, if that order is specified.
         * Exceptions thrown by the action are relayed to the caller.
         *
         * @implSpec
         * <p>The default implementation behaves as if:
         * <pre>{@code
         *     while (hasNext())
         *         action.accept(next());
         * }</pre>
         *
         * @param action The action to be performed for each element
         * @throws NullPointerException if the specified action is null
         * @since 1.8
         */
        default void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            while (hasNext())
                action.accept(next());
        }
    }

    遍历最主要需要 next() 和 hasNext() 方法, remove方法用来删除元素.

    假定现在有两种菜单,一种用数组实现,一种用ArrayList实现, 我们需要遍历这两种的数据, 因为这两种实现不同,所以遍历的时候我们需要两种遍历代码,一种是遍历数组,一种遍历 ArrayLIst

    这时候我们就可以使用迭代器模式

    我们先定义一个统一的菜单元素, 早餐  和 午餐都使用到了它

    public class MenuItem {
        String name;
        String description;
        boolean Vegetarian;
        double price;
        public MenuItem(String name, String description, boolean vegetarian, double price) {
            super();
            this.name = name;
            this.description = description;
            Vegetarian = vegetarian;
            this.price = price;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getDescription() {
            return description;
        }
        public void setDescription(String description) {
            this.description = description;
        }
        public boolean isVegetarian() {
            return Vegetarian;
        }
        public void setVegetarian(boolean vegetarian) {
            Vegetarian = vegetarian;
        }
        public double getPrice() {
            return price;
        }
        public void setPrice(double price) {
            this.price = price;
        }
        
    }

    存放了四个变量,就是用来记录一个菜单的没什么特殊含义.

    在定义一个菜单接口,用来获取迭代器

    package iterator;
    
    import java.util.Iterator;
    
    public interface Menu {
        Iterator createIterator();
    }

    先定义煎饼,因为煎饼可以当作早餐,用 ArrayLIst 实现,并且实现了Menu接口,要返回一个迭代器

    package iterator;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    
    public class PancakeHouseMenu implements Menu{
        ArrayList menuItems;
    
        public PancakeHouseMenu() {
            menuItems = new ArrayList();
            addItem("A", "A", true, 2.00);
            addItem("B", "B", true, 3.00);
            addItem("C", "C", false, 112.00);
            addItem("D", "D", true, 3.00);
        }
        
        public void addItem (String name, String description, boolean vegetarian, double price) {
            menuItems.add(new MenuItem(name, description, vegetarian, price));
        }
        
        public Iterator createIterator() {
            return menuItems.iterator();
        }
        
        public ArrayList getMenuItems() {
            return menuItems;
        }
        
    }

    ArrayList 已经实现了 Iterator 接口, 所以 在 createIterator 方法中 我们直接返回 ArrayList 的接口

    接下来我们定义午餐

    package iterator;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    
    public class DinerMenu implements Menu{
        static final int MAX_ITEMS = 6;
        
        int numberOfItems = 0;
        
        
        MenuItem[] menuItems;
        public DinerMenu() {
            menuItems = new MenuItem[MAX_ITEMS];
            addItem("A", "A", true, 1.0);
            addItem("B", "B", true, 2.0);
            addItem("C", "C", true, 3.0);
            addItem("D", "D", true, 4.0);
        }
        
        
        public void addItem (String name, String description, boolean vegetarian, double price) {
            if(numberOfItems >= MAX_ITEMS) {
                System.out.println("menu is full");
            } else {
                menuItems[numberOfItems] = new MenuItem(name, description, vegetarian, price);
                numberOfItems ++;
            }
            
        }
        
        public Iterator createIterator() {
            return new DinerMenuIterator(menuItems);
        }
    
        
    }

    因为 数组没有iterator,所以需要我们自己定义

    定义 午餐的 Iterator

    package iterator;
    
    import java.util.Iterator;
    
    public class DinerMenuIterator implements Iterator{
        MenuItem[] items;
        int position;
        public DinerMenuIterator(MenuItem[] items) {
            this.items = items;
        }
        @Override
        public boolean hasNext() {
            if (position >= items.length || items[position] == null) {
                return false;
            }
            return true;
        }
    
    
        @Override
        public Object next() {
            return items[position++];
        }
        
        
    }

    在午餐菜单中的createIterator方法中返回.

    定义一个服务员遍历上面两种菜单

    public class Waitress {
        //用来获取各种菜单,比如 早餐,午餐等
        ArrayList menus;
        
        public Waitress(ArrayList menus) {
            this.menus = menus;
        }
    
        public void printMenu() {
            Iterator menuIterator = menus.iterator();
            while (menuIterator.hasNext()) {
                Menu menu = (Menu) menuIterator.next();
                printMenu(menu.createIterator());
            }
        }
        
        private void printMenu(Iterator it) {
            while (it.hasNext()) {
                MenuItem menuItem = (MenuItem) it.next();
                System.out.print(menuItem.getName() + " --- ");
                System.out.print(menuItem.getPrice() + " --- ");
                System.out.println(menuItem.getDescription());
            }
        }
    }

    这边我们可以看到,传入的菜单都是Menu接口,不依赖于具体的菜单,如果依赖具体的菜单的话新增加菜单就需要修改大量代码,而且通过 Iterator  我们可以遍历 菜单内的元素, 而不需要写很多循环.

    这边稍微改进了以下,用一个list来接收各种菜单,这样在增加菜单的时候就不需要修改waitress的代码,做到了开闭原则.

    测试

    package iterator;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    
    public class MenuTestDrive {
        public static void main(String[] args) {
            PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
            DinerMenu dinerMenu = new DinerMenu();
            CafeMenu cafeMenu = new CafeMenu();
            ArrayList list = new ArrayList();
            list.add(pancakeHouseMenu);
            list.add(dinerMenu);
            list.add(cafeMenu);
            Waitress waitress = new Waitress(list);
            waitress.printMenu();
        }
    }
    --------breakfase---------
    A --- 2.0 --- A
    B --- 3.0 --- B
    C --- 112.0 --- C
    D --- 3.0 --- D
    --------dinner---------
    A --- 1.0 --- A
    B --- 2.0 --- B
    C --- 3.0 --- C
    D --- 4.0 --- D

    3.总结

    迭代器还是比较简单的一个模式, 接口中主要包含 next  和  hasNext方法即可.   我们平常也可以通过 加强 for 循环来遍历 Collection  框架中的集合, 如果我们想要遍历某个数组,直接实现 Iterator 接口即可.

    对于开闭原则理解又加深了,在拓展功能或者修改功能时候要做到不修改以前的代码,这样的设计才是一个好的设计.

  • 相关阅读:
    Socket开发框架之消息的回调处理
    Socket开发框架之数据加密及完整性检查
    Socket开发框架之数据传输协议
    Socket开发框架之框架设计及分析
    C#进行Visio二次开发之文件导出及另存Web页面
    Winform混合式开发框架的特点总结
    代码生成工具Database2Sharp中增加视图的代码生成以及主从表界面生成功能
    基于Metronic的Bootstrap开发框架经验总结(9)--实现Web页面内容的打印预览和保存操作
    基于C#的MongoDB数据库开发应用(4)--Redis的安装及使用
    基于C#的MongoDB数据库开发应用(3)--MongoDB数据库的C#开发之异步接口
  • 原文地址:https://www.cnblogs.com/lishuaiqi/p/11291839.html
Copyright © 2011-2022 走看看