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

news/2024/12/22 15:15:03/

情景:

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

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

菜单选项类

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.ppmy.cn/news/1042002.html

相关文章

进入银行科技部半年,已经丧失跳槽的能力了

大家好&#xff0c;我是锋哥!&#xff01; 学弟分享 我是一个杭州双非的本科生&#xff0c;2022届毕业之后进了某银行的科技部工作&#xff0c;年包 20w。 当时想着在银行也算是一份安稳的工作&#xff0c;因此选择了给钱最多的一个&#xff0c;想着自己走上了金融 科技的赛…

NVIDIA vGPU License许可服务器高可用全套部署秘籍

第1章 前言 近期遇到比较多的场景使用vGPU&#xff0c;比如Citrix 3D场景、Horizon 3D场景&#xff0c;还有AI等&#xff0c;都需要使用显卡设计研发等&#xff0c;此时许可服务器尤为重要&#xff0c;许可断掉会出现掉帧等情况&#xff0c;我们此次教大家部署HA许可服务器。 …

idea安装插件JRebel

一、安装插件 二、下载代理服务并启动 首先下载服务&#xff0c;网址 Releases ilanyu/ReverseProxy (kgithub.com) 下载后直接双击运行&#xff0c;杀毒软件可以会报警告&#xff0c;直接忽略就行 启动好&#xff0c;等着就行&#xff0c;接下来会用到。 三、在线获取GUID …

海外问卷脚本机器人哪里哪里有?是真的吗?

大家好&#xff0c;我是橙河老师&#xff0c;今天讲一讲海外问卷项目能不能用脚本操作&#xff1f; 最近没怎么写文章&#xff0c;确实比较忙。我本人每天至少要面对5-10个客户咨询项目&#xff0c;每隔一段时间&#xff0c;都会有人问我&#xff1a;操作海外问卷有没有脚本&a…

【使用群晖远程链接drive挂载电脑硬盘】

文章目录 前言1.群晖Synology Drive套件的安装1.1 安装Synology Drive套件1.2 设置Synology Drive套件1.3 局域网内电脑测试和使用 2.使用cpolar远程访问内网Synology Drive2.1 Cpolar云端设置2.2 Cpolar本地设置2.3 测试和使用 3. 结语 前言 群晖作为专业的数据存储中心&…

C语言编程:最小二乘法拟合直线

本文研究通过C语言实现最小二乘法拟合直线。 文章目录 1 引入2 公式推导3 C语言代码实现4 测试验证5 总结 1 引入 最小二乘法&#xff0c;简单来说就是根据一组观测得到的数值&#xff0c;寻找一个函数&#xff0c;使得函数与观测点的误差的平方和达到最小。在工程实践中&…

算法通关村第4关【白银】| 栈的经典算法问题

1.括号匹配问题 思路&#xff1a;将左括号压入栈中&#xff0c;遍历字符串&#xff0c;当遇到右括号就出栈&#xff0c;判断是否是匹配的一对&#xff0c;不是就返回false&#xff08;因为按照顺序所以当遇到右括号出栈一定要是匹配的&#xff09;。使用Map来简化ifelse clas…

《Java极简设计模式》第04章:建造者模式(Builder)

作者&#xff1a;冰河 星球&#xff1a;http://m6z.cn/6aeFbs 博客&#xff1a;https://binghe.gitcode.host 文章汇总&#xff1a;https://binghe.gitcode.host/md/all/all.html 源码地址&#xff1a;https://github.com/binghe001/java-simple-design-patterns/tree/master/j…