《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博客
相关文章:
《HeadFirst设计模式(第二版)》第九章代码——迭代器模式
情景: 一家早餐店和一家午餐点准备合并在一起,两家的点菜的菜单实现方式如下: 首先,他们的菜单选项都基于同一个类: 菜单选项类 package Chapter9_IteratorPattern.Origin;/*** Author 竹心* Date 2023/8/17**/public class Men…...
Electron入门,项目启动。
electron 简单介绍: 实现:HTML/CSS/JS桌面程序,搭建跨平台桌面应用。 electron 官方文档: [https://electronjs.org/docs] 本文是基于以下2篇文章且自行实践过的,可行性真实有效。 文章1: https://www.cnbl…...
深入理解索引B+树的基本原理
目录 1. 引言 2. 为什么要使用索引? 3. 索引的概述 4. 索引的优点是什么? 4.1 降低数据库的IO成本,提高数据查找效率 4.2 保证数据库每一行数据的唯一性 4.3 加速表与表之间的连接 4.4 减少查询中分组与排序的执行时间 5. 索引的缺点…...
vue3 简易用对话框实现点击头像放大查看
设置头像悬停手势 img:hover{cursor: pointer;}效果: 编写对话框 <el-dialog class"bigAvatar"style"border-radius: 4px;"v-model"deleteDialogVisible"title"查看头像"top"5px"><div><img src&…...
opencv 矩阵运算
1.矩阵乘(*) Mat mat1 Mat::ones(2,3,CV_32FC1);Mat mat2 Mat::ones(3,2,CV_32FC1);Mat mat3 mat1 * mat2; //矩阵乘 结果 2.元素乘法或者除法(mul) Mat m Mat::ones(2, 3, CV_32FC1);m.at<float>(0, 1) 3;m.at…...
第四章 字符串part01
344.反转字符串 public void reverseString(char[] s) {int len s.length;int left 0;int right len-1;while (left < right){char tmp s[right];s[right] s[left];s[left] tmp;left;right--;} }反转字符串II 注意String不可变,因此可使用char数组或者St…...
Python3内置函数大全
吐血整理 Python3内置函数大全 1.abs()函数2.all()函数3.any()函数4.ascii()函数5.bin()函数6.bool()函数7.bytes()函数8.challable()函数9.chr()函数10.classmethod()函数11.complex()函数12.complie()函数13.delattr()函数14.dict()函数15.dir()函数16.divmod()函数17.enumer…...
什么是“新型基础设施”?建设重点是什么?
一是信息基础设施。主要是指基于新一代信息技术演化生成的基础设施,比如,以5G、物联网、工业互联网、卫星互联网为代表的通信网络基础设施,以人工智能、云计算、区块链等为代表的新技术基础设施,以数据中心、智能计算中心为代表的…...
混杂接口模式---vlan
策略在两个地方可以用--1、重发布 2、bgp邻居 2、二层可以干的,三层也可以干 3、未知单播:交换机的MAC地址表的记录保留时间是5分钟,电脑的ARP表的记录保留时间是2小时 4、route recursive-lookup tunnel 华为默认对于bgp学习来的路由不开启标…...
Greenplum多级分区表添加分区报错ERROR: no partitions specified at depth 2
一般来说,我们二级分区表都会使用模版,如果没有使用模版特性,那么就会报ERROR: no partitions specified at depth 2类似的错误。因为没有模版,必须要显式指定分区。 当然我们在建表的时候,如果没有指定,那…...
EV PV AC SPI CPI TCPI
SPI EV / PV CPI EV / ACCPI 1.25 SPI 0.8 PV 10 000 BAC 100 000EV PV * SPI 10 000 * 0.8 8000 AC EV / CPI 8000 / 1.25 6400TCPI (BAC - EV) / (BAC -AC) (100 000 - 8 000) / (100 000 - 6 400) 92 000 / 93 600 0.98290598...
【电商领域】Axure在线购物商城小程序原型图,品牌自营垂直电商APP原型
作品概况 页面数量:共 60 页 兼容软件:Axure RP 9/10,不支持低版本 应用领域:网上商城、品牌自营商城、商城模块插件 作品申明:页面内容仅用于功能演示,无实际功能 作品特色 本作品为品牌自营网上商城…...
Cpp基础Ⅰ之编译、链接
1 C是如何工作的 工具:Visual Studio 1.1 预处理语句 在.cpp源文件中,所有#字符开头的语句为预处理语句 例如在下面的 Hello World 程序中 #include<iostream>int main() {std::cout <"Hello World!"<std::endl;std::cin.get…...
用户新增预测(Datawhale机器学习AI夏令营第三期)
文章目录 简介任务1:跑通Baseline实操并回答下面问题:如果将submit.csv提交到讯飞比赛页面,会有多少的分数?代码中如何对udmp进行了人工的onehot? 任务2.1:数据分析与可视化编写代码回答下面的问题…...
RGOS日常管理操作
RGOS日常管理操作 一、前言二、RGOS平台概述2.1、锐捷设备的常用登陆方式2.2、使用Console登入2.3、Telnet远程管理2.4、SSH远程管理2.5、登陆软件:SecureCRT 三、CLI命令行操作3.1、CLI命令行基础3.2、CLI模式3.3、CLI模式互换3.4、命令行特性3.4.1、分屏显示3.4.2…...
阿里云使用WordPress搭建个人博客
手把手教你使用阿里云服务器搭建个人博客 一、免费创建服务器实例 1.1 点击试用 点击试用会需要你创建服务器实例,直接选择默认的操作系统即可,点击下一步 1.2 修改服务器账号密码 二、创建云数据库实例 2.1 免费获取云数据库使用 2.2 实例列表页 在…...
供应链安全和第三方风险管理:讨论如何应对供应链中的安全风险,以及评估和管理第三方合作伙伴可能带来的威胁
第一章:引言 在当今数字化时代,供应链的安全性越来越受到重视。企业的成功不仅仅依赖于产品和服务的质量,还取决于供应链中的安全性。然而,随着供应链越来越复杂,第三方合作伙伴的参与也带来了一系列安全风险。本文将…...
《Java极简设计模式》第04章:建造者模式(Builder)
作者:冰河 星球:http://m6z.cn/6aeFbs 博客:https://binghe.gitcode.host 文章汇总:https://binghe.gitcode.host/md/all/all.html 源码地址:https://github.com/binghe001/java-simple-design-patterns/tree/master/j…...
Go download
https://go.dev/dl/https://golang.google.cn/dl/...
2023年Java核心技术面试第四篇(篇篇万字精讲)
目录 八. 对比Vector,ArrayList, LinkedList有何区别? 8.1 典型回答 8.1.1 Vector: 8.1.2 ArrayList : 8.1.3 LinkedList 8.2 考察点分析: 8.2.1 不同容器类型适合的场景 八. 对比Vector,ArrayList, Linke…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
leetcode73-矩阵置零
leetcode 73 思路 记录 0 元素的位置:遍历整个矩阵,找出所有值为 0 的元素,并将它们的坐标记录在数组zeroPosition中置零操作:遍历记录的所有 0 元素位置,将每个位置对应的行和列的所有元素置为 0 具体步骤 初始化…...
深度解析云存储:概念、架构与应用实践
在数据爆炸式增长的时代,传统本地存储因容量限制、管理复杂等问题,已难以满足企业和个人的需求。云存储凭借灵活扩展、便捷访问等特性,成为数据存储领域的主流解决方案。从个人照片备份到企业核心数据管理,云存储正重塑数据存储与…...
C#最佳实践:为何优先使用as或is而非强制转换
C#最佳实践:为何优先使用as或is而非强制转换 在 C# 的编程世界里,类型转换是我们经常会遇到的操作。就像在现实生活中,我们可能需要把不同形状的物品重新整理归类一样,在代码里,我们也常常需要将一个数据类型转换为另…...
CppCon 2015 学习:Simple, Extensible Pattern Matching in C++14
什么是 Pattern Matching(模式匹配) ❝ 模式匹配就是一种“描述式”的写法,不需要你手动判断、提取数据,而是直接描述你希望的数据结构是什么样子,系统自动判断并提取。❞ 你给的定义拆解: ✴ Instead of …...
