情景:
一家早餐店和一家午餐点准备合并在一起,两家的点菜的菜单实现方式如下:
首先,他们的菜单选项都基于同一个类:
菜单选项类
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
现在问题来了,如果菜单中存在子菜单,那么又该如何实现呢?
很明显上面的方法已经不适用了,重写菜单代码才行。
这里就引出了组合模式: