当前位置: 首页 > news >正文

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

情景:

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

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

菜单选项类

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

早餐店初始菜单

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

午餐店初始菜单:

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来实现,后者使用数组来实现。

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

服务员类初始

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),迭代器负责直接处理菜单的遍历等功能,然后服务员通过这个迭代器来使用菜单,成功解耦。

简介

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

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

自定义的迭代器

自定义迭代器接口

package Chapter9_IteratorPattern.MyIterator;/*** @Author 竹心* @Date 2023/8/17**/public interface Iterator {//提供一个统一的迭代器接口,在用户和对象集合之间加入迭代器,//迭代器中含有遍历集合的具体操作,不需要关心如何实现boolean hasNext();MenuItem next();
}

早餐迭代器:

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;}@Overridepublic MenuItem next() {MenuItem menuItem = items.get(position);position++;return menuItem;}@Overridepublic boolean hasNext() {if(position >= items.size() || items.get(position) == null){return false;}else{return true;}}
}

使用迭代器的早餐菜单

加入一个新方法即可:

    public Iterator createIterator(){return new PancakeHouseIterator(menuItems);}

午餐迭代器:

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;}@Overridepublic MenuItem next() {MenuItem menuItem = items[position];position++;return menuItem;}@Overridepublic boolean hasNext() {if(position >= items.length || items[position] == null){return false;}else{return true;}}
}

使用迭代器的午餐菜单:

同理:

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

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

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()+"  ");}}
}

测试类:

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的迭代器)。

修改早餐类:

添加头文件:

import java.util.Iterator;

然后修改方法:

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

修改午餐迭代器类:

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;}@Overridepublic MenuItem next() {MenuItem menuItem = items[position];position++;return menuItem;}@Overridepublic boolean hasNext() {if(position >= items.length || items[position] == null){return false;}else{return true;}}@Overridepublic void remove() {//java自带的迭代器是由remove()方法的,所以这里必须要实现throw new UnsupportedOperationException("you can not remove it!");}
}

添加Menu接口:

package Chapter9_IteratorPattern.JavaIterator;import java.util.Iterator;/*** @Author 竹心* @Date 2023/8/18**/public interface Menu {public Iterator<?> createIterator();
}

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

public class DinerMenu implements Menu{...}public class PancakeHouseMenu implements Menu{...}

修改服务员类:

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自带迭代器的晚餐菜单:

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

修改后的服务员类:

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()+"  ");}}
}

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

优化后的服务员类

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

http://www.lryc.cn/news/129500.html

相关文章:

  • Electron入门,项目启动。
  • 深入理解索引B+树的基本原理
  • vue3 简易用对话框实现点击头像放大查看
  • opencv 矩阵运算
  • 第四章 字符串part01
  • Python3内置函数大全
  • 什么是“新型基础设施”?建设重点是什么?
  • 混杂接口模式---vlan
  • Greenplum多级分区表添加分区报错ERROR: no partitions specified at depth 2
  • EV PV AC SPI CPI TCPI
  • 【电商领域】Axure在线购物商城小程序原型图,品牌自营垂直电商APP原型
  • Cpp基础Ⅰ之编译、链接
  • 用户新增预测(Datawhale机器学习AI夏令营第三期)
  • RGOS日常管理操作
  • 阿里云使用WordPress搭建个人博客
  • 供应链安全和第三方风险管理:讨论如何应对供应链中的安全风险,以及评估和管理第三方合作伙伴可能带来的威胁
  • 《Java极简设计模式》第04章:建造者模式(Builder)
  • Go download
  • 2023年Java核心技术面试第四篇(篇篇万字精讲)
  • 数字化时代,数据仓库和商业智能BI系统演进的五个阶段
  • 【【Verilog典型电路设计之FIFO设计】】
  • JAVA设计模式总结之23种设计模式
  • Flutter 测试小结
  • docker build -t 和 docker build -f 区别
  • Java 项目日志实例基础:Log4j
  • K8S应用笔记 —— 签发自签名证书用于Ingress的https配置
  • webpack 和 ts 简单配置及使用
  • MATLAB算法实战应用案例精讲-【图像处理】交并比
  • [Machine Learning] decision tree 决策树
  • 【数学建模】-- 数学规划模型