迭代器模式

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 接口即可.

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

 

posted @ 2019-08-03 00:29  随意的马蒂洛克  阅读(219)  评论(0编辑  收藏  举报