《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博客

相关推荐
数据小爬虫@19 分钟前
Java爬虫实战:深度解析Lazada商品详情
java·开发语言
咕德猫宁丶21 分钟前
探秘Xss:原理、类型与防范全解析
java·网络·xss
F-2H2 小时前
C语言:指针4(常量指针和指针常量及动态内存分配)
java·linux·c语言·开发语言·前端·c++
苹果酱05672 小时前
「Mysql优化大师一」mysql服务性能剖析工具
java·vue.js·spring boot·mysql·课程设计
_oP_i3 小时前
Pinpoint 是一个开源的分布式追踪系统
java·分布式·开源
mmsx3 小时前
android sqlite 数据库简单封装示例(java)
android·java·数据库
武子康3 小时前
大数据-258 离线数仓 - Griffin架构 配置安装 Livy 架构设计 解压配置 Hadoop Hive
java·大数据·数据仓库·hive·hadoop·架构
豪宇刘4 小时前
MyBatis的面试题以及详细解答二
java·servlet·tomcat
秋恬意4 小时前
Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
java·数据库·mybatis
FF在路上5 小时前
Knife4j调试实体类传参扁平化模式修改:default-flat-param-object: true
java·开发语言