介绍
迭代器模式(Iterator Pattern),它用来遍历集合对象。可以把它看作成一个“容器”,它里面装了很多的对象。
它,主要是用来解决不同的方式遍历整个对象的问题。比如一个,是用数组来存储对象,另一个是用ArrayList存储。
两个不同的对象集合,采用遍历的方式也不同。所以,会写很多重复性代码。而使用迭代器模式,可以把具体的遍历方式给隐藏起来。统一使用使用迭代器遍历。
例子
现在给一个例子来理解它,现在有两个菜单,一个是煎饼菜单、另一个是餐厅的菜单。煎饼菜单采用的是集合来存储对象,而另一个采用的数组来存储对象。
public class MenuItem {
private String name;
private String description;
private boolean vegetarian;
private double price;
public MenuItem(String name, String description, boolean vegetarian, double price) {
this.name = name;
this.description = description;
this.vegetarian = vegetarian;
this.price = price;
}
// Getter Setter
}
// 菜单1
public class PancakeHouseMenu {
private final ArrayList<MenuItem> menuItems;
public PancakeHouseMenu() {
menuItems = new ArrayList<>();
addItem("煎蛋早餐饼", "外焦里嫩的早餐饼", true, 2.99);
addItem("煎蛋早餐饼2", "外焦里嫩的早餐饼2", false, 3.99);
addItem("煎蛋早餐饼3", "外焦里嫩的早餐饼3", true, 3.49);
addItem("煎蛋早餐饼4", "外焦里嫩的早餐饼4", true, 4.99);
// 继续添加其他的项目
}
public void addItem(String name, String description, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
menuItems.add(menuItem);
}
public ArrayList<MenuItem> getMenuItems() {
return menuItems;
}
}
// 菜单2
public class DinerMenu {
private static final int MAX_ITEMS = 6;
private final MenuItem[] menuItems;
private int numberOfItems = 0;
public DinerMenu() {
menuItems = new MenuItem[MAX_ITEMS];
addItem("热狗", "好吃的热狗", false, 3.05);
addItem("老火靓汤", "味道鲜满的老火靓汤", true, 2.95);
addItem("晚餐套餐", "balabalabala......", false, 3.05);
addItem("晚餐套餐2", "balabalabala......", false, 3.05);
addItem("晚餐套餐3", "balabalabala......", false, 3.05);
addItem("晚餐套餐4", "balabalabala......", false, 3.05);
}
public void addItem(String name, String description, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
if (numberOfItems >= MAX_ITEMS) {
System.out.println("菜单已满!");
} else {
menuItems[numberOfItems] = menuItem;
numberOfItems += 1;
}
}
public MenuItem[] getMenuItems() {
return menuItems;
}
}
循环打印出菜单:
public static void main(String[] args) {
PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
ArrayList<MenuItem> breakfastItems = pancakeHouseMenu.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());
}
DinerMenu dinerMenu = new DinerMenu();
MenuItem[] lunchItems = dinerMenu.getMenuItems();
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 interface Iterator {
boolean hasNext();
MenuItem next();
}
实现迭代器与改造菜单:
public class DinerMenuIterator implements Iterator {
private final MenuItem[] items;
private int position = 0;
public DinnerMenuIterator(MenuItem[] items) {
this.items = items;
}
@Override
public boolean hasNext() {
// 判断组数里是否还有对象
return position < items.length && items[position] != null;
}
@Override
public MenuItem next() {
MenuItem menuItems = items[position];
position += 1;
return menuItems;
}
}
public class DinerMenu {
private static final int MAX_ITEMS = 6;
private final MenuItem[] menuItems;
private int numberOfItems = 0;
// 初始化
// 添加菜单的方法
// 创建迭代器
public Iterator createIterator() {
return new DinerMenuIterator(menuItems);
}
}
public class PancakeHouseMenuIterator implements Iterator {
private final ArrayList<MenuItem> items;
private int position = 0;
public PancakeHouseMenuIterator(ArrayList<MenuItem> items) {
this.items = items;
}
@Override
public boolean hasNext() {
return position < items.size() && items.get(position) != null;
}
@Override
public MenuItem next() {
MenuItem item = items.get(position);
position += 1;
return item;
}
}
public class PancakeHouseMenu {
private final ArrayList<MenuItem> menuItems;
// 初始化
// 添加菜单的方法
// 创建迭代器
public Iterator createIterator() {
return new PancakeHouseMenuIterator(menuItems);
}
}
由于,我们的菜单都是服务员提供给客户看的,所以,还需要添加一个服务员类:
public class Waitress {
private final PancakeHouseMenu pancakeHouseMenu;
private final DinerMenu dinerMenu;
public Waitress(PancakeHouseMenu pancakeHouseMenu, DinerMenu dinerMenu) {
this.pancakeHouseMenu = pancakeHouseMenu;
this.dinerMenu = dinerMenu;
}
public void printMenu() {
Iterator pancakeHouseMenuIterator = pancakeHouseMenu.createIterator();
Iterator dinerMenuIterator = dinerMenu.createIterator();
printMenu(pancakeHouseMenuIterator);
System.out.println("-----菜单1-----");
printMenu(dinerMenuIterator);
System.out.println("-----菜单2-----");
}
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());
}
}
}
// 测试一下
public static void main(String[] args) {
Waitress waitress = new Waitress(new PancakeHouseMenu(), new DinerMenu());
waitress.printMenu();
}
就这样,我们利用迭代器模式,简化了一些代码,尽管增加了一些代码,但它并不会让我们重复性的去写循环便利的代码,因为这实在太麻烦了。就算新增一种菜单,对代码的改动也不会很大。
但目前代码的缺点是Waitress类与菜单的耦合性很高,新增菜单,我们又要对这个类进行改动。
还有,在Java中已经有了一个java.util.Iterator
迭代,我们无需再去重新实现,将现有的Iterator改成java.util.Iterator
之后,我们来优化Waitress类。
// 菜单接口
public interface Menu {
Iterator<MenuItem> createIterator();
void addItem(String name, String description, boolean vegetarian, double price);
}
// 更改现有的菜单类,我们通过面向接口来编程
public class PancakeHouseMenu implements Menu {
// ...
}
public class DinerMenu implements Menu {
// ...
}
// 优化Waitress类
public class Waitress {
private ArrayList<Menu> menus;
public Waitress(ArrayList<Menu> menus) {
this.menus = menus;
}
public void printMenu() {
// 遍历
for (Menu menu : menus) {
printMenu(menu.createIterator());
}
}
private void printMenu(Iterator<MenuItem> iterator) {
while (iterator.hasNext()) {
// ...
}
}
}
// 我们来测试一下
public static void main(String[] args) {
ArrayList<Menu> menus = new ArrayList<>();
menus.add(new PancakeHouseMenu());
menus.add(new DinerMenu());
Waitress waitress = new Waitress(menus);
waitress.printMenu();
}
这样,就可以让我们的Waitress类变得更加灵活,新增的菜单,只需要添加到菜单集合中即可。
组合模式
现在,要求加上子菜单,还要支持菜单的选中。但,现在的实现,无法支持我们的需求。重写现在的实现,显然是不太可能的。
但我们,可以用用组合模式,它可以解决我们的问题。
组合模式的定义:组合模式允许你将对象组合成树形结构,来表现“整体/部分”。组合能让客户以一致的方式处理一个对象以及对象集合。
虽然,我们重写所有的代码不太可能。但我们只需要会退几步,稍稍改一下。
菜单组合类:
// 组合类
public abstract class MenuComponent {
public void add(MenuComponent component) {
// 先不提供具体的实现
throw new UnsupportedOperationException();
}
public MenuComponent getChild(int i) {
throw new UnsupportedOperationException();
}
public String getName() {
throw new UnsupportedOperationException();
}
public String getDescription() {
throw new UnsupportedOperationException();
}
public double getPrice() {
throw new UnsupportedOperationException();
}
public boolean isVegetarian() {
throw new UnsupportedOperationException();
}
public void print() {
throw new UnsupportedOperationException();
}
}
实现组合菜单:
public class Menu extends MenuComponent {
private ArrayList<MenuComponent> menuComponents = new ArrayList<>();
private String name;
private String description;
public Menu(String name, String description) {
this.name = name;
this.description = description;
}
@Override
public void add(MenuComponent component) {
menuComponents.add(component);
}
@Override
public MenuComponent getChild(int i) {
return menuComponents.get(i);
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return description;
}
@Override
public void print() {
System.out.print("\n" + getName());
System.out.println(", " + getDescription());
System.out.println("-----------------------------");
Iterator<MenuComponent> iterator = menuComponents.iterator();
while (iterator.hasNext()) {
MenuComponent menuComponent = iterator.next();
menuComponent.print();
}
}
}
public class PancakeHouseMenu extends Menu {
public PancakeHouseMenu(String name, String description) {
super(name, description);
// 菜单项
}
public void addItem(String name, String description, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
super.add(menuItem);
}
}
public class DinerMenu extends Menu {
public DinerMenu(String name, String description) {
super(name, description);
// 菜单项
}
private void addItem(String name, String description, boolean isVegetarian, double price) {
MenuItem menuItem = new MenuItem(name, description, isVegetarian, price);
super.add(menuItem);
}
}
实现了,组合菜单后测试一下,看看结果如何:
public static void main(String[] args) {
MenuComponent pancakeHouseMenu = new PancakeHouseMenu("PANCAKE HOUSE MENU", "breakfast");
MenuComponent diner = new DinerMenu("DINER MENU", "diner");
MenuComponent allMenus = new Menu("ALL MENUS", "ALL menus combined");
allMenus.add(pancakeHouseMenu);
allMenus.add(diner);
// 加入具体的菜单项
diner.add(new MenuItem("意大利面","balabala...", true, 3.89));
// 加入其他的菜单...
Waitress waitress = new Waitress(allMenus);
waitress.printMenu();
}
结果如下:
好了,这样就已经完成,我们的需求了。
当,有数个对象时,它们彼此之间有“整体/部分”的关系,并且想要一致对待这些对象时,就要使用组合模式。