Head First设计模式读书笔记八 第九章上 迭代器模式

版权声明:本文为博主原创文章,转载请注明出处 https://blog.csdn.net/u011109881/article/details/82716310

之前的总结:

https://blog.csdn.net/u011109881/article/details/59677544

个人觉得本章节,HeadFirst讲的没有之前看到的网站讲的清晰,至少在迭代器这部分是的;不过,head first书中讲的更详细些。本节还是采用Head First中的例子来描述迭代器模式。

情景再现

现有两家店的实现方式不同,一个采用ArrayList,一个采用数组;现需要对所有菜单遍历,那么做法一般会是如下形式:
首先菜单的model类:

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;
        this.vegetarian = vegetarian;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    public boolean isVegetarian() {
        return vegetarian;
    }

    public double getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return "name =" + name + " description " + description
                + " isVegetarian " + vegetarian + " price= " + price;
    }
}

两家店菜单的实现:

public class DinerMenu {
    static final int MAX_ITEMS = 6;
    public int numberOfItems = 0;
    MenuItem[] menuItems;

    public DinerMenu() {
        menuItems = new MenuItem[MAX_ITEMS];
        addMenuItem("Diner Menu1", "xxxxx", true, 3.99);
        addMenuItem("Diner with meat", "xxxxx", false, 4.99);
        addMenuItem("Diner Menu2", "xxxxx", true, 4.49);
        addMenuItem("Diner with pork", "xxxx", false, 5.99);
    }

    public MenuItem[] getMenuItems() {
        return menuItems;
    }



    public void addMenuItem(String name, String description,
            boolean vegetarian, double price) {
        MenuItem item = new MenuItem(name, description, vegetarian, price);
        if (numberOfItems >= MAX_ITEMS) {
            System.out.println("menu is full! Can't add item to menu any more");
        } else {
            menuItems[numberOfItems] = item;
            numberOfItems++;
        }
    }
}
public class PancakeHouseMenu {
    ArrayList<MenuItem> menuItems;

    public PancakeHouseMenu() {
        menuItems = new ArrayList<MenuItem>();
        addMenuItem("Pancake Breakfast1", "xxxxx", true, 2.99);
        addMenuItem("Pancake Breakfast with meat", "xxxxx", false, 3.99);
        addMenuItem("Pancake Breakfast", "xxxxx", true, 3.49);
        addMenuItem("Pancake Breakfast with pork", "xxxx", false, 4.99);
    }

    public ArrayList<MenuItem> getMenuItems() {
        return menuItems;
    }

    public void addMenuItem(String name,String description,boolean vegetarian,double price) {
        MenuItem item = new MenuItem(name, description, vegetarian, price);
        menuItems.add(item);
    }

}

输出类的普通实现:

public class Printer1 {

    public static void main(String[] args) {
        PancakeHouseMenu breakfastMenu = new PancakeHouseMenu();
        ArrayList<MenuItem> breakfastList = breakfastMenu.getMenuItems();
        for (MenuItem item : breakfastList) {
            System.out.println(item.toString());
        }

        DinerMenu dinerMenu = new DinerMenu();
        MenuItem[] dinerList = dinerMenu.getMenuItems();
        for (int i = 0; i < dinerMenu.numberOfItems; i++) {
            System.out.println(dinerList[i].toString());
        }
    }

}

这里可以看到,当实现种类有多少种,则循环需要执行多少次,也就意味着代码会根据实际情况变得复杂;要避免这种情况,就要用到本章的设计模式迭代器了。

添加迭代器

对于数组,没有现成的迭代器,需要自定义迭代器:(当然也可以继承util包里的Iterator)

package iterator;

public interface Iterator {
    boolean hasNext();
    Object next();
}
package iterator;

import bean.MenuItem;

public class DinerMenuIterator implements Iterator {
    MenuItem[] items;
    int pos = 0;

    public DinerMenuIterator(MenuItem[] items) {
        this.items = items;
    }

    public boolean hasNext() {
        if (pos >= items.length || items[pos] == null) {
            return false;
        } else {
            return true;
        }
    }

    public Object next() {
        MenuItem item = items[pos];
        pos++;
        return item;
    }

}

对DinerMenu稍作修改,去掉getItems方法,添加获取Iterator方法

package bean;

import iterator.DinerMenuIterator;

public class DinerMenu {
    static final int MAX_ITEMS = 6;
    public int numberOfItems = 0;
    MenuItem[] menuItems;
    DinerMenuIterator iterator;//变动

    public DinerMenu() {
        menuItems = new MenuItem[MAX_ITEMS];
        addMenuItem("Diner Menu1", "xxxxx", true, 3.99);
        addMenuItem("Diner with meat", "xxxxx", false, 4.99);
        addMenuItem("Diner Menu2", "xxxxx", true, 4.49);
        addMenuItem("Diner with pork", "xxxx", false, 5.99);
        iterator = new DinerMenuIterator(menuItems);//变动
    }

    //
    // public MenuItem[] getMenuItems() {
    // return menuItems;
    // }
    //

    public void addMenuItem(String name, String description,
            boolean vegetarian, double price) {
        MenuItem item = new MenuItem(name, description, vegetarian, price);
        if (numberOfItems >= MAX_ITEMS) {
            System.out.println("menu is full! Can't add item to menu any more");
        } else {
            menuItems[numberOfItems] = item;
            numberOfItems++;
        }
    }

    //新增方法
    public DinerMenuIterator getIterator() {
        return this.iterator;
    }
}

而对于breakfastMenu则简单的多,因为breakfastMenu实现是ArrayList,可以直接返回ArrayList的itorator。注意,此处代码和书中不同,此处的Iterator和上面自定义的Iterator并不是同一个Iterator

public class PancakeHouseMenu {
    ArrayList<MenuItem> menuItems;

    public PancakeHouseMenu() {
        menuItems = new ArrayList<MenuItem>();
        addMenuItem("Pancake Breakfast1", "xxxxx", true, 2.99);
        addMenuItem("Pancake Breakfast with meat", "xxxxx", false, 3.99);
        addMenuItem("Pancake Breakfast", "xxxxx", true, 3.49);
        addMenuItem("Pancake Breakfast with pork", "xxxx", false, 4.99);
    }

//  public ArrayList<MenuItem> getMenuItems() {
//      return menuItems;
//  }

    public void addMenuItem(String name,String description,boolean vegetarian,double price) {
        MenuItem item = new MenuItem(name, description, vegetarian, price);
        menuItems.add(item);
    }

    public Iterator<MenuItem> getIterator(){
        return menuItems.iterator();
    }

}

测试方法略作修改:

public class Printer2 {

    public static void main(String[] args) {
        PancakeHouseMenu breakfastMenu = new PancakeHouseMenu();
        Iterator<MenuItem> iterator1 = breakfastMenu.getIterator();
        while (iterator1.hasNext()) {
            System.out.println(iterator1.next());
        }

        DinerMenu dinerMenu = new DinerMenu();
        iterator.Iterator iterator2 = dinerMenu.getIterator();
        while (iterator2.hasNext()) {
            System.out.println(iterator2.next());
        }
    }

}

看上去形式统一了呢。
此时的类结构:
这里写图片描述

再改进:全部使用util的Iterator

Java util包中的Iterator和我们自定义的Iterator其实是类似的,之所以先用自定义的Iterator,是因为这样做可以更了解Java的Iterator的实现原理
Java自带的Itorator:

package java.util;

import java.util.function.Consumer;

/**
 * An iterator over a collection.  {@code Iterator} takes the place of
 * {@link Enumeration} in the Java Collections Framework.  Iterators
 * differ from enumerations in two ways:
 *
 * <ul>
 *      <li> Iterators allow the caller to remove elements from the
 *           underlying collection during the iteration with well-defined
 *           semantics.
 *      <li> Method names have been improved.
 * </ul>
 *
 * <p>This interface is a member of the
 * <a href="{@docRoot}/../technotes/guides/collections/index.html">
 * Java Collections Framework</a>.
 *
 * @param <E> the type of elements returned by this iterator
 *
 * @author  Josh Bloch
 * @see Collection
 * @see ListIterator
 * @see Iterable
 * @since 1.2
 */
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());
    }
}

可以看到,主体和自定义的Iterator还是类似的。
对于PancakeHouseMenu基本没有变动

public class PancakeHouseMenu {
    ArrayList<MenuItem> menuItems;

    public PancakeHouseMenu() {
        menuItems = new ArrayList<MenuItem>();
        addMenuItem("Pancake Breakfast1", "xxxxx", true, 2.99);
        addMenuItem("Pancake Breakfast with meat", "xxxxx", false, 3.99);
        addMenuItem("Pancake Breakfast", "xxxxx", true, 3.49);
        addMenuItem("Pancake Breakfast with pork", "xxxx", false, 4.99);
    }

    public void addMenuItem(String name,String description,boolean vegetarian,double price) {
        MenuItem item = new MenuItem(name, description, vegetarian, price);
        menuItems.add(item);
    }

    public Iterator<MenuItem> createIterator(){
        return menuItems.iterator();
    }

}

DinerMenuIterator的基本实现不需要变动,就换个导包即可

public class DinerMenuIterator implements Iterator<MenuItem> {
    MenuItem[] items;
    int pos = 0;

    public DinerMenuIterator(MenuItem[] items) {
        this.items = items;
    }

    public boolean hasNext() {
        if (pos >= items.length || items[pos] == null) {
            return false;
        } else {
            return true;
        }
    }

    public MenuItem next() {
        MenuItem item = items[pos];
        pos++;
        return item;
    }
}

dinerMenu则也需要重新导入Iterator的包

public class DinerMenu {
    static final int MAX_ITEMS = 6;
    public int numberOfItems = 0;
    MenuItem[] menuItems;
    Iterator<MenuItem> iterator;

    public DinerMenu() {
        menuItems = new MenuItem[MAX_ITEMS];
        addMenuItem("Diner Menu1", "xxxxx", true, 3.99);
        addMenuItem("Diner with meat", "xxxxx", false, 4.99);
        addMenuItem("Diner Menu2", "xxxxx", true, 4.49);
        addMenuItem("Diner with pork", "xxxx", false, 5.99);
        iterator = new DinerMenuIterator(menuItems);
    }

    public void addMenuItem(String name, String description,
            boolean vegetarian, double price) {
        MenuItem item = new MenuItem(name, description, vegetarian, price);
        if (numberOfItems >= MAX_ITEMS) {
            System.out.println("menu is full! Can't add item to menu any more");
        } else {
            menuItems[numberOfItems] = item;
            numberOfItems++;
        }
    }

    public Iterator<MenuItem> createIterator() {
        return this.iterator;
    }
}

测试类稍作整修

    public static void main(String[] args) {
        PancakeHouseMenu breakfastMenu = new PancakeHouseMenu();
        Iterator<MenuItem> iterator1 = breakfastMenu.createIterator();
        while (iterator1.hasNext()) {
            System.out.println(iterator1.next());
        }

        DinerMenu dinerMenu = new DinerMenu();
        Iterator<MenuItem> iterator2 = dinerMenu.createIterator();
        while (iterator2.hasNext()) {
            System.out.println(iterator2.next());
        }
    }

输出结果仍然不变:

name =Pancake Breakfast1 description xxxxx isVegetarian true price= 2.99
name =Pancake Breakfast with meat description xxxxx isVegetarian false price= 3.99
name =Pancake Breakfast description xxxxx isVegetarian true price= 3.49
name =Pancake Breakfast with pork description xxxx isVegetarian false price= 4.99
name =Diner Menu1 description xxxxx isVegetarian true price= 3.99
name =Diner with meat description xxxxx isVegetarian false price= 4.99
name =Diner Menu2 description xxxxx isVegetarian true price= 4.49
name =Diner with pork description xxxx isVegetarian false price= 5.99

但是类的结构图却已经变化:
这里写图片描述
注意,此处类图与书中不同(主要是没有实现Menu接口以及女招待对象),属个人理解。如有错误,请指出

迭代器模式的作用

  1. 迭代器的出现,让我们无需关心相关model的实现结构
  2. 将数据的遍历抽离出来,使得单一职责原则能够很好的实现。

迭代器的替换

事实上,JDK 1.5新增了增强的for循环,让我们对于Collections的集合类,遍历更加方便,对于任意Collections,我们都可以这样遍历:

        for(Object m :items){
            System.out.println("xxxx");
        }

这样即使不使用Iterator,我们仍然可以遍历元素

迭代器的多次使用

之前,我的实现与书中不同,以为实现Menu接口并无甚用,后面才发现,是为了遍历Menu而做的铺垫。书中最后的迭代器实例,先对存放Menu集合的list进行遍历,之后,对每个menu内的Item项进行遍历。因此,有两次Iterator的使用。现实现如下:(需对上述代码略作修改)
增加Menu接口

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

让DinerMenu PancakeHouseMenu都实现Menu接口;因为之前是直接写了createIterator方法,因此这次只需在后面加上 implements Menu即可

添加Waitress类的实现

public class Waitress {
    ArrayList<Menu> menus;
    public Waitress(Menu dinerMenu,Menu pancakeHouseMenu) {
        menus = new ArrayList<Menu>();
        menus.add(dinerMenu);
        menus.add(pancakeHouseMenu);
    }

    public void printMenu(){
        Iterator<Menu> menusIterator = menus.iterator();
        while(menusIterator.hasNext()){
            printMenu(menusIterator.next().createIterator());
        }

//      for(Menu menu : menus){
//          printMenu(menu.createIterator());
//      }
    }

    private void printMenu(Iterator<MenuItem> iterator) {
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

修改测试类

public class Printer2 {

    public static void main(String[] args) {
        PancakeHouseMenu breakfastMenu = new PancakeHouseMenu();
        DinerMenu dinerMenu = new DinerMenu();
        Waitress w = new Waitress(dinerMenu, breakfastMenu);
        w.printMenu();
    }

}

输出不变

name =Diner Menu1 description xxxxx isVegetarian true price= 3.99
name =Diner with meat description xxxxx isVegetarian false price= 4.99
name =Diner Menu2 description xxxxx isVegetarian true price= 4.49
name =Diner with pork description xxxx isVegetarian false price= 5.99
name =Pancake Breakfast1 description xxxxx isVegetarian true price= 2.99
name =Pancake Breakfast with meat description xxxxx isVegetarian false price= 3.99
name =Pancake Breakfast description xxxxx isVegetarian true price= 3.49
name =Pancake Breakfast with pork description xxxx isVegetarian false price= 4.99

猜你喜欢

转载自blog.csdn.net/u011109881/article/details/82716310