设计模式——(11)迭代器模式

第十一章 迭代器模式

1.1 一般的菜单遍历

餐厅都有自己的一份菜单,而菜单由菜单项组成,每个菜单项描述菜名、菜价和描述等信息。当需要遍历时,一般会想到如下遍历的方式:

首先定义一个菜单项 MenuItem

package headfirst.designpatterns.iterator.generalmenu;

public class MenuItem{
    String name;
    String description;
    boolean vegetarian;
    double price;

    public MenuItem(String name, String description, boolean vegetarian, double price) {
        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;
    }
}

然后定义一个煎饼屋菜单 PancakeHouseMenu,使用 ArrayList 存储菜单项。

package headfirst.designpatterns.iterator.generalmenu;

import java.util.ArrayList;

public class PancakeHouseMenu implements Menu{
    ArrayList<MenuItem> menuItems;

    public PancakeHouseMenu() {
        menuItems = new ArrayList<MenuItem>();

        addItem("K&B's Pancake Breakfast",
                "Pancakes with scrambled eggs, and toast",
                true,
                2.99);

        addItem("Regular Pancake Breakfast",
                "Pancakes with fried eggs, sausage",
                false,
                2.99);

        addItem("Blueberry Pancakes",
                "Pancakes made with fresh blueberries, and blueberry syrup",
                true,
                3.49);

        addItem("Waffles",
                "Waffles, with your choice of blueberries or strawberries",
                true,
                3.59);

    }

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

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


}

同时,定义一份DinerMenu,使用数组存储菜单项。

package headfirst.designpatterns.iterator.generalmenu;

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

    public DinerMenu() {
        menuItems = new MenuItem[MAX_ITEMS];

        addItem("Vegetarian BLT",
                "(Fakin') Bacon with lettuce & tomato on whole wheat", true, 2.99);
        addItem("BLT",
                "Bacon with lettuce & tomato on whole wheat", false, 2.99);
        addItem("Soup of the day",
                "Soup of the day, with a side of potato salad", false, 3.29);
        addItem("Hotdog",
                "A hot dog, with saurkraut, relish, onions, topped with cheese",
                false, 3.05);
        addItem("Steamed Veggies and Brown Rice",
                "Steamed vegetables over brown rice", true, 3.99);
        addItem("Pasta",
                "Spaghetti with Marinara Sauce, and a slice of sourdough bread",
                true, 3.89);
    }

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

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

}

在客户代码中,遍历两份菜单的菜单项:

package headfirst.designpatterns.iterator.generalmenu;

import java.util.List;

public class Waitress {
    Menu pancakeHouseMenu;
    Menu dinerMenu;

    public Waitress(Menu pancakeHouseMenu, Menu dinerMenu) {
        this.pancakeHouseMenu = pancakeHouseMenu;
        this.dinerMenu = dinerMenu;
    }

    public void printMenu(){
        List<MenuItem> breakfastItems = ((PancakeHouseMenu) pancakeHouseMenu).getMenuItems();
        // 在对集合进行遍历的时候,breakfastItems.size() 、breakfastItems.get(i) 是变化的,可能因breakfastItems中采用存储菜单项的集合变化而变化,因此需要封装。
        for (int i = 0; i < breakfastItems.size(); i++) {
            MenuItem menuItem = breakfastItems.get(i);
            System.out.print(menuItem.getName() + ", ");
            System.out.print(menuItem.getPrice() + " -- ");
            System.out.println(menuItem.getDescription());
        }

        System.out.println("-------------------------------");

        MenuItem[] lunchItems = ((DinerMenu) dinerMenu).getMenuItems();
        for (int i = 0; i < lunchItems.length; i++) {
            MenuItem menuItem = lunchItems[i];
            System.out.print(menuItem.getName() + ", ");
            System.out.print(menuItem.getPrice() + " -- ");
            System.out.println(menuItem.getDescription());
        }
    }


}

在对集合进行遍历的时候,breakfastItems.size() 、breakfastItems.get(i) 是变化的,可能因breakfastItems中采用存储菜单项的集合变化而变化,因此需要对代码中变化的部分进行封装。由此,引入迭代器模式。

1.2 使用迭代器模式进行菜单遍历

我们再明确一下菜单与菜单项的关系,菜单负责管理了诸多个菜单项,把菜单项对象堆起来成为一个集合。

为了使用迭代器模式来遍历菜单中的菜单项,我们需要为每个菜单创建一个迭代器对象,以用来遍历。

因此,设计的类图如下图所示。

请添加图片描述

  • Waitress 是客户代码;

  • 客户代码需要遍历菜单项,因此依赖PancakeHouseMenu 和 DinerMenu两个菜单对象;

  • 客户代码是通过Iterator来遍历菜单项对象的;

  • 每个菜单对象通过创建具体的菜单迭代器对象进行遍历。

定义迭代器接口Iterator:

package headfirst.designpatterns.iterator.iteratormenu;

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

定义了菜单接口,每个菜单都应该具有创建对应迭代器的能力:

package headfirst.designpatterns.iterator.iteratormenu;

public interface Menu {
    Iterator createIterator();
}

定义菜单 DinerMenu 实现了 Menu 接口,包含了createIterator()方法,以创建迭代器对象:

package headfirst.designpatterns.iterator.iteratormenu;

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

    public DinerMenu() {
        menuItems = new MenuItem[MAX_ITEMS];


        addItem("Vegetarian BLT",
                "(Fakin') Bacon with lettuce & tomato on whole wheat", true, 2.99);
        addItem("BLT",
                "Bacon with lettuce & tomato on whole wheat", false, 2.99);
        addItem("Soup of the day",
                "Soup of the day, with a side of potato salad", false, 3.29);
        addItem("Hotdog",
                "A hot dog, with saurkraut, relish, onions, topped with cheese",
                false, 3.05);
        addItem("Steamed Veggies and Brown Rice",
                "Steamed vegetables over brown rice", true, 3.99);
        addItem("Pasta",
                "Spaghetti with Marinara Sauce, and a slice of sourdough bread",
                true, 3.89);
    }

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

    @Override
    public Iterator createIterator() {
        Iterator dinerMenuIterator = new DinerMenuIterator(menuItems);
        return dinerMenuIterator;
    }
}

定义了菜单 PancakeHouseMenu 实现了 Menu 接口,包含了createIterator()方法,以创建迭代器对象:

package headfirst.designpatterns.iterator.iteratormenu;

import java.util.ArrayList;
import java.util.List;

public class PancakeHouseMenu implements Menu {
    List<MenuItem> menuItems;

    public PancakeHouseMenu() {
        menuItems = new ArrayList<MenuItem>();

        addItem("K&B's Pancake Breakfast",
                "Pancakes with scrambled eggs, and toast",
                true,
                2.99);

        addItem("Regular Pancake Breakfast",
                "Pancakes with fried eggs, sausage",
                false,
                2.99);

        addItem("Blueberry Pancakes",
                "Pancakes made with fresh blueberries, and blueberry syrup",
                true,
                3.49);

        addItem("Waffles",
                "Waffles, with your choice of blueberries or strawberries",
                true,
                3.59);

    }

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

    @Override
    public Iterator createIterator() {
        Iterator pancakeHouseMenuIterator = new PancakeHouseMenuIterator(menuItems);
        return pancakeHouseMenuIterator;
    }
}

再定义具体的菜单迭代器对象 DinerMenuIterator 和 PancakeHouseMenuIterator:

package headfirst.designpatterns.iterator.iteratormenu;

public class DinerMenuIterator implements Iterator {

    MenuItem[] items;

    int position =0;

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

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

    @Override
    public Object next() {
        MenuItem menuItem = items[position];
        position = position+1;
        return menuItem;
    }
}

package headfirst.designpatterns.iterator.iteratormenu;

import java.util.List;

public class PancakeHouseMenuIterator implements Iterator{

    List<MenuItem> items;

    public PancakeHouseMenuIterator(List<MenuItem> items) {
        this.items = items;
    }

    int position =0;

    @Override
    public boolean hasNext() {
        return items.size() > position;
    }

    @Override
    public Object next() {
        return items.get(position++);
    }
}

使用客户代码进行测试:

package headfirst.designpatterns.iterator.iteratormenu;

public class Waitress {
    Menu pancakeHouseMenu;
    Menu dinerMenu;

    public Waitress(Menu pancakeHouseMenu, Menu dinerMenu) {
        this.pancakeHouseMenu = pancakeHouseMenu;
        this.dinerMenu = dinerMenu;
    }

    public void printMenu(){
        Iterator dinerMenuIterator = dinerMenu.createIterator();
        // 即使菜单组织菜单项使用的数据结构变化,例如List -> Map,在维护时,这部分代码也不会变化。
        while (dinerMenuIterator.hasNext()){
            MenuItem menuItem = (MenuItem)dinerMenuIterator.next();
            printMenuItem(menuItem);
        }

        System.out.println("------------------------");

        Iterator pancakeHouseMenuIterator = pancakeHouseMenu.createIterator();
        while (pancakeHouseMenuIterator.hasNext()){
            MenuItem menuItem = (MenuItem)pancakeHouseMenuIterator.next();
            printMenuItem(menuItem);
        }
    }

    public void printMenuItem(MenuItem menuItem){
        System.out.print(menuItem.getName() + ", ");
        System.out.print(menuItem.getPrice() + " -- ");
        System.out.println(menuItem.getDescription());
    }
}

输出结果:

Vegetarian BLT, 2.99 -- (Fakin') Bacon with lettuce & tomato on whole wheat
BLT, 2.99 -- Bacon with lettuce & tomato on whole wheat
Soup of the day, 3.29 -- Soup of the day, with a side of potato salad
Hotdog, 3.05 -- A hot dog, with saurkraut, relish, onions, topped with cheese
Steamed Veggies and Brown Rice, 3.99 -- Steamed vegetables over brown rice
Pasta, 3.89 -- Spaghetti with Marinara Sauce, and a slice of sourdough bread
------------------------
K&B's Pancake Breakfast, 2.99 -- Pancakes with scrambled eggs, and toast
Regular Pancake Breakfast, 2.99 -- Pancakes with fried eggs, sausage
Blueberry Pancakes, 3.49 -- Pancakes made with fresh blueberries, and blueberry syrup
Waffles, 3.59 -- Waffles, with your choice of blueberries or strawberries

当然,我们在这里使用了自定义的 Iterator 接口,也可以使用 java.util.Iterator 包下的接口。

1.4 总结

迭代器模式:提供了一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

迭代器模式的类图如下:
请添加图片描述

参考

[1] Freeman E. Head First 设计模式[M] 中国电力出版社.
[2] 菜鸟教程.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值