《HeadFirst设计模式(第二版)》第九章代码——迭代器模式

情景:

一家早餐店和一家午餐点准备合并在一起,两家的点菜的菜单实现方式如下:

首先,他们的菜单选项都基于同一个类:

菜单选项类

java 复制代码
package Chapter9_IteratorPattern.Origin;

/**
 * @Author 竹心
 * @Date 2023/8/17
 **/

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;
    }
}

早餐店初始菜单

java 复制代码
package Chapter9_IteratorPattern.Origin;

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

/**
 * @Author 竹心
 * @Date 2023/8/17
 **/

public class PancakeHouseMenu {
    List<MenuItem> menuItems;

    public PancakeHouseMenu(){
        this.menuItems = new ArrayList<MenuItem>();
        addItem("K&B's Pancake Breakfast",
                "Pancake with scrambled eggs and toast",
                true,
                2.99);
        addItem("Regular Pancake Breakfast",
                "Pancake with fired eggs, sausage",
                false,
                2.99);
        addItem("BlueBerry Pancake",
                "Pancake made with fresh blueberries",
                true,
                3.49);
        addItem("Waffles",
                "Waffles with your choice of blueberries of strawberries",
                true,
                3.59);
    }

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

    public List<MenuItem> getMenuItems(){
        return this.menuItems;
    }
}

午餐店初始菜单:

java 复制代码
package Chapter9_IteratorPattern.Origin;

/**
 * @Author 竹心
 * @Date 2023/8/17
 **/

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

    public DinerMenu(){
        this.menuItems = new MenuItem[MAX_ITEMS];
        addItem("aaa","food1",true,2.99);
        addItem("bbb","food2",true,2.99);
        addItem("ccc","food3",true,3.29);
        addItem("ddd","food4",true,3.05);
    }

    public void addItem(String name,String description,
                        boolean vegetarian,double price){
        MenuItem menuItem = new MenuItem(name,description,vegetarian,price);
        if(this.numberOfItems >= MAX_ITEMS){
            System.out.println("Sorry! Menu is full!");
        }else{
            this.menuItems[this.numberOfItems] = menuItem;
            this.numberOfItems++;
        }
    }

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

可以得知:前者使用List来实现,后者使用数组来实现。

这时候,如果不采取任何方法加以更改,新餐厅的服务员将要这样使用两个菜单:

服务员类初始

java 复制代码
package Chapter9_IteratorPattern.Origin;

import java.util.List;

/**
 * @Author 竹心
 * @Date 2023/8/17
 **/

public class Waitress {
    public void printMenu(){
        //遍历菜单
        PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
        List<MenuItem> breakfastItems = pancakeHouseMenu.getMenuItems();

        DinerMenu dinerMenu = new DinerMenu();
        MenuItem[] lunchItems = dinerMenu.getMenuItems();

        for(int i= 0;i<breakfastItems.size();++i){
            MenuItem menuItem = breakfastItems.get(i);
            System.out.println(menuItem.getName()+"  ");
            System.out.println(menuItem.getPrice()+"  ");
            System.out.println(menuItem.getDescription()+"  ");
        }

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

    public static void main(String[] args) {
        Waitress waitress = new Waitress();
        waitress.printMenu();
    }
}

由此可见:服务员类和两个菜单类直接接触,既违反封装原理,还违背了面向接口编码的原理,同时极其不利于维护。

迭代器模式

这时可以采用迭代器模式:在两个菜单和服务员之间加入一个迭代器(iterator),迭代器负责直接处理菜单的遍历等功能,然后服务员通过这个迭代器来使用菜单,成功解耦。

简介

迭代器提供一种方式,可以访问一个聚合对象中的元素而又不暴露其潜在实现。

同时把遍历的任务放到迭代器上而不是聚合上,这就简化了聚合的接口和实现(让聚合只需负责管理对象集合即可),满足单一责任原则。

自定义的迭代器

自定义迭代器接口

java 复制代码
package Chapter9_IteratorPattern.MyIterator;

/**
 * @Author 竹心
 * @Date 2023/8/17
 **/

public interface Iterator {

    //提供一个统一的迭代器接口,在用户和对象集合之间加入迭代器,
    //迭代器中含有遍历集合的具体操作,不需要关心如何实现
    boolean hasNext();
    MenuItem next();
}

早餐迭代器:

java 复制代码
package Chapter9_IteratorPattern.MyIterator;

import java.util.List;

/**
 * @Author 竹心
 * @Date 2023/8/17
 **/

public class PancakeHouseIterator implements Iterator{
    List<MenuItem> items;
    int position = 0;

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

    @Override
    public MenuItem next() {
        MenuItem menuItem = items.get(position);
        position++;
        return menuItem;
    }

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

使用迭代器的早餐菜单

加入一个新方法即可:

java 复制代码
    public Iterator createIterator(){
        return new PancakeHouseIterator(menuItems);
    }

午餐迭代器:

java 复制代码
package Chapter9_IteratorPattern.MyIterator;

/**
 * @Author 竹心
 * @Date 2023/8/17
 **/

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

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

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

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

使用迭代器的午餐菜单:

同理:

java 复制代码
    public Iterator createIterator(){
        //提供一个接口使得迭代器获取到该集合
        return new DinerMenuIterator(menuItems);
    }

使用自定义迭代器的服务员类:

java 复制代码
package Chapter9_IteratorPattern.MyIterator;

/**
 * @Author 竹心
 * @Date 2023/8/17
 **/

public class Waitress {
    PancakeHouseMenu pancakeHouseMenu;
    DinerMenu dinerMenu;

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

    public void printMenu(){
        Iterator pancakeMenuIterator = pancakeHouseMenu.createIterator();
        Iterator dinerIterator = dinerMenu.createIterator();

        System.out.println("MENU\n----\nBREAKFAST");
        printMenu(pancakeMenuIterator);
        System.out.println("\nLUNCH");
        printMenu(dinerIterator);

    }

    private void printMenu(Iterator iterator){
        while(iterator.hasNext()){
            MenuItem menuItem = iterator.next();
            System.out.println(menuItem.getName()+"  ");
            System.out.println(menuItem.getPrice()+"  ");
            System.out.println(menuItem.getDescription()+"  ");
        }
    }
}

测试类:

java 复制代码
package Chapter9_IteratorPattern.MyIterator;

/**
 * @Author 竹心
 * @Date 2023/8/17
 **/

public class MenuTestDrive {
    public static void main(String[] args) {
        PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
        DinerMenu dinerMenu = new DinerMenu();

        Waitress waitress = new Waitress(pancakeHouseMenu,dinerMenu);
        waitress.printMenu();
    }
}

使用JAVA自带的迭代器

因为早餐菜单使用了ArrayList,它有iterator()方法返回一个迭代器(所以这里可以删除掉它的自定义迭代器),然而午餐菜单是用数组实现的,没有这个方法,所以午餐类还是需要使用自定义的迭代器("继承"于java的迭代器)。

修改早餐类:

添加头文件:

java 复制代码
import java.util.Iterator;

然后修改方法:

java 复制代码
    public Iterator<MenuItem> createIterator(){
        return menuItems.iterator();
    }

修改午餐迭代器类:

java 复制代码
package Chapter9_IteratorPattern.JavaIterator;
import java.util.Iterator;

/**
 * @Author 竹心
 * @Date 2023/8/17
 **/

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

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

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

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

    @Override
    public void remove() {
        //java自带的迭代器是由remove()方法的,所以这里必须要实现
        throw new UnsupportedOperationException("you can not remove it!");
    }
}

添加Menu接口:

java 复制代码
package Chapter9_IteratorPattern.JavaIterator;

import java.util.Iterator;

/**
 * @Author 竹心
 * @Date 2023/8/18
 **/

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

记得分别在午餐菜单类和早餐菜单类的类声明那里加上对Menu的实现:

java 复制代码
public class DinerMenu implements Menu{...}

public class PancakeHouseMenu implements Menu{...}

修改服务员类:

java 复制代码
package Chapter9_IteratorPattern.JavaIterator;
import java.util.Iterator;
/**
 * @Author 竹心
 * @Date 2023/8/17
 **/

public class Waitress {
    Menu pancakeHouseMenu;
    Menu dinerMenu;

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

    public void printMenu(){
        Iterator<?> pancakeMenuIterator = pancakeHouseMenu.createIterator();
        Iterator<?> dinerIterator = dinerMenu.createIterator();

        System.out.println("MENU\n----\nBREAKFAST");
        printMenu(pancakeMenuIterator);
        System.out.println("\nLUNCH");
        printMenu(dinerIterator);

    }

    private void printMenu(Iterator iterator){
        while(iterator.hasNext()){
            MenuItem menuItem = (MenuItem) iterator.next();
            System.out.println(menuItem.getName()+"  ");
            System.out.println(menuItem.getPrice()+"  ");
            System.out.println(menuItem.getDescription()+"  ");
        }
    }
}

情景扩展1

好了,现在又有一家咖啡店并入餐厅,并在晚上提供服务,这家咖啡店也有它独特的菜单实现方式:使用哈希表!接下来要将它加入迭代器的使用中(这里将其称为晚餐菜单):

使用JAVA自带迭代器的晚餐菜单:

java 复制代码
package Chapter9_IteratorPattern.AddCafe;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @Author 竹心
 * @Date 2023/8/18
 **/

public class CafeMenu implements Menu{
    HashMap<String, MenuItem> menuItems = new HashMap<String, MenuItem>();

    public CafeMenu() {
        addItem("Veggie Burger and Air Fries",
                "Veggie burger on a whole wheat bun, lettuce, tomato, and fries",
                true, 3.99);
        addItem("Soup of the day",
                "A cup of the soup of the day, with a side salad",
                false, 3.69);
        addItem("Burrito",
                "A large burrito, with whole pinto beans, salsa, guacamole",
                true, 4.29);
    }

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

//    public Map<String, MenuItem> getItems() {
//        return menuItems;
//    }

    public Iterator<MenuItem> createIterator() {
        //获取哈希表中的集合的迭代器
        return menuItems.values().iterator();
    }
}

修改后的服务员类:

java 复制代码
package Chapter9_IteratorPattern.AddCafe;

import java.util.Iterator;
/**
 * @Author 竹心
 * @Date 2023/8/17
 **/

public class Waitress {
    Menu pancakeHouseMenu;
    Menu dinerMenu;
    Menu cafeMenu;

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

    public void printMenu(){
        Iterator<?> pancakeMenuIterator = pancakeHouseMenu.createIterator();
        Iterator<?> dinerIterator = dinerMenu.createIterator();
        Iterator<?> cafeIterator = cafeMenu.createIterator();

        System.out.println("MENU\n----\nBREAKFAST");
        printMenu(pancakeMenuIterator);
        System.out.println("\nLUNCH");
        printMenu(dinerIterator);
        System.out.println("\nDINNER");
        printMenu(cafeIterator);

    }

    private void printMenu(Iterator iterator){
        while(iterator.hasNext()){
            MenuItem menuItem = (MenuItem) iterator.next();
            System.out.println(menuItem.getName()+"  ");
            System.out.println(menuItem.getPrice()+"  ");
            System.out.println(menuItem.getDescription()+"  ");
        }
    }
}

现在,我们发现,如果菜单越来越多,服务员类涉及到的操作也越来越多,所以这里可以对服务员类进行优化:

优化后的服务员类

java 复制代码
package headfirst.designpatterns.iterator.transition;
import java.util.*;
  

/**
 * @Author 竹心
 * @Date 2023/8/18
 **/


public class Waitress {
	ArrayList<Menu> menus;
     
  
	public Waitress(ArrayList<Menu> menus) {
		this.menus = menus;
	}
   
	public void printMenu() {
		Iterator<?> menuIterator = menus.iterator();
		while(menuIterator.hasNext()) {
			Menu menu = (Menu)menuIterator.next();
			printMenu(menu.createIterator());
		}
	}
   
	void printMenu(Iterator<?> iterator) {
		while (iterator.hasNext()) {
			MenuItem menuItem = (MenuItem)iterator.next();
			System.out.print(menuItem.getName() + ", ");
			System.out.print(menuItem.getPrice() + " -- ");
			System.out.println(menuItem.getDescription());
		}
	}
}  

情景扩展2

现在问题来了,如果菜单中存在子菜单,那么又该如何实现呢?

很明显上面的方法已经不适用了,重写菜单代码才行。

这里就引出了组合模式:

《HeadFirst设计模式(第二版)》第九章代码------组合模式_轩下小酌的博客-CSDN博客

相关推荐
缺点内向3 小时前
Java:创建、读取或更新 Excel 文档
java·excel
带刺的坐椅4 小时前
Solon v3.4.7, v3.5.6, v3.6.1 发布(国产优秀应用开发框架)
java·spring·solon
四谎真好看5 小时前
Java 黑马程序员学习笔记(进阶篇18)
java·笔记·学习·学习笔记
桦说编程5 小时前
深入解析CompletableFuture源码实现(2)———双源输入
java·后端·源码
java_t_t5 小时前
ZIP工具类
java·zip
lang201509286 小时前
Spring Boot优雅关闭全解析
java·spring boot·后端
pengzhuofan6 小时前
第10章 Maven
java·maven
百锦再7 小时前
Vue Scoped样式混淆问题详解与解决方案
java·前端·javascript·数据库·vue.js·学习·.net
刘一说7 小时前
Spring Boot 启动慢?启动过程深度解析与优化策略
java·spring boot·后端
壹佰大多7 小时前
【spring如何扫描一个路径下被注解修饰的类】
java·后端·spring