披萨店和咖啡店合并了
主管告诉小明,披萨店和咖啡店合并了,小明说你怎么知道啊?因为他们在合并是遇到了问题,由于他们的菜单不一样,所以在服务员在使用时很麻烦,这不新店找到了小明他们帮助解决这个问题。
菜单上的单项(庆幸的是他们菜单上菜品是用的相同的类):
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 double getPrice() {
return price;
}
public boolean isVegetarian() {
return vegetarian;
}
}
披萨店菜单:
public class PizzaMenu implements Menu {
ArrayList menuItems;
public PizzaMenu() {
menuItems = new ArrayList();
addItem("芝士披萨", "芝士加番茄", true, 2.99);
addItem("榴莲披萨", "榴莲味", true, 3.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 getMenuItems() {
return menuItems;
}
// other menu methods here
}
咖啡店菜单:
public class CoffeeMenu implements Menu {
static final int MAX_ITEMS = 6;
int numberOfItems = 0;
MenuItem[] menuItems;
public CoffeeMenu() {
menuItems = new MenuItem[MAX_ITEMS];
addItem("焦糖玛奇朵", "", true, 12);
addItem("拿铁", "", true, 10);
}
public void addItem(String name, String description, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
if (numberOfItems >= MAX_ITEMS) {
System.err.println("Sorry, menu is full! Can't add item to menu");
} else {
menuItems[numberOfItems] = menuItem;
numberOfItems = numberOfItems + 1;
}
}
public MenuItem[] getMenuItems() {
return menuItems;
}
// other menu methods here
}
合并以后,服务员在使用两个菜单必须这样
public class Waitress {
PizzaMenu pizzaMenu;
CoffeeMenu coffeeMenu;
public Waitress(PizzaMenu pizzaMenu, Menu coffeeMenu) {
this.pizzaMenu = pizzaMenu;
this.coffeeMenu = coffeeMenu;
}
public void printMenu() {
ArrayList pizzaMenuItems = pizzaMenu. getMenuItems();
// 打印披萨菜单
MenuItem[] cafeMenuItems = coffeeMenu. getMenuItems();
// 打印咖啡菜单
}
}
对于两个菜单,他们用不同的集合实现了内部的菜品管理,一个是ArrayList,一个是数组,所以服务员在操作时,就必须按照它们数据结构不同的操作方式去操作这些数据,最严重的问题时,如果再合并一家其他的餐厅,而该餐厅用的又是Map来管理的,那么不但要修改服务员的代码,还需要用map的遍历方式去阅读菜单。这样服务员和菜单的耦合就太严重了,所以必须要有一种方式防止这个的问题,小明问主管,有什么方式呢?主管说,迭代器模式可以很好的解决这个问题,让我们开始动手优化吧!
首先,创造一个迭代器:
public interface Iterator {
boolean hasNext();
Object next();
}
分别针对两个菜单都实现它:
披萨菜单迭代器:
public class PizzaMenuIterator implements Iterator {
ArrayList items;
int position = 0;
public PizzaMenuIterator(ArrayList items) {
this.items = items;
}
public Object next() {
Object object = items.get(position);
position = position + 1;
return object;
}
public boolean hasNext() {
if (position >= items.size()) {
return false;
} else {
return true;
}
}
}
咖啡菜单迭代器:
public class CoffeeMenuIterator implements Iterator {
MenuItem[] items;
int position = 0;
public CoffeeMenuIterator(MenuItem[] items) {
this.items = items;
}
public Object next() {
MenuItem menuItem = items[position];
position = position + 1;
return menuItem;
}
public boolean hasNext() {
if (position >= items.length || items[position] == null) {
return false;
} else {
return true;
}
}
}
新的披萨店菜单:
public class PizzaMenu implements Menu {
ArrayList menuItems;
public PizzaMenu() {
menuItems = new ArrayList();
addItem("芝士披萨", "芝士加番茄", true, 2.99);
addItem("榴莲披萨", "榴莲味", true, 3.99);
}
public void addItem(String name, String description, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
menuItems.add(menuItem);
}
public Iterator createIterator() {
return new PizzaMenuIterator(menuItems);
}
// other menu methods here
}
新的咖啡店菜单:
public class CoffeeMenu implements Menu {
static final int MAX_ITEMS = 6;
int numberOfItems = 0;
MenuItem[] menuItems;
public CoffeeMenu() {
menuItems = new MenuItem[MAX_ITEMS];
addItem("焦糖玛奇朵", "", true, 12);
addItem("拿铁", "", true, 10);
}
public void addItem(String name, String description, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
if (numberOfItems >= MAX_ITEMS) {
System.err.println("Sorry, menu is full! Can't add item to menu");
} else {
menuItems[numberOfItems] = menuItem;
numberOfItems = numberOfItems + 1;
}
}
public Iterator createIterator() {
return new CoffeeMenuIterator(menuItems);
}
// other menu methods here
}
两个新的菜单将之前获取具体数据集的方式修改成了createIterator()方法,我们看看新的方式下,服务员再使用他们时的用法:
public class Waitress {
PizzaMenu pizzaMenu;
CoffeeMenu coffeeMenu;
public Waitress(PizzaMenu pizzaMenu, Menu coffeeMenu) {
this.pizzaMenu = pizzaMenu;
this.coffeeMenu = coffeeMenu;
}
public void printMenu() {
Iterator pizzaIterator = pizzaMenu.createIterator();
printMenu(pizzaIterator);
Iterator coffeeIterator = coffeeMenu.createIterator();
printMenu(coffeeIterator);
}
private 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());
}
}
}
和之前的方式相比,服务员浏览(打印所有菜品)菜单只用了一个方法printMenu(Iterator iterator),如果以后新增了用其他集合管理的菜单,只需要用一个新的迭代器去实现相应数据集合的迭代即可,服务员只需要拿到这个新的菜单就可以浏览了,而不用去修改服务员内部代码。这不是适配器吗?稍等,先看看定义,然后我们再总结他们的区别。
定义
提供一种方法顺序访问集合对象中的各个元素,而又不暴露其内部的表示。
角色
- 迭代器
- 迭代器实现
- 聚合 主要是定义createIterator方法
- 聚合的实现
迭代器和集合同存亡
深入认识一下迭代器
首先回答上面那个问题,它和适配器有什么区别,其实从定义可以看出来,迭代是为了访问集合中的元素,而适配器将不满足要求的接口适配为满足要求的接口。而且有很重要的一点,适配器不仅仅是为了统一接口,它的作用还体现了一个重要的原则单一职责原则,像上面我们的菜单,它主要的职责是菜单本身,如果将访问菜单也加入菜单类的功能,那么就违背了单一职责原则,所以将这个功能用单独的一个迭代器去实现,将更好的体现菜单本身的职责。
实际的运用
我们刚才实现了两个迭代器,分别针对ArrayList和数组,其实JDK本身已经给我们在ArrayList提供了这个功能:
@Override public Iterator<E> iterator() {
return new ArrayListIterator();
}
private class ArrayListIterator implements Iterator<E> {
/** Number of elements remaining in this iteration */
private int remaining = size;
/** Index of element that remove() would remove, or -1 if no such elt */
private int removalIndex = -1;
/** The expected modCount value */
private int expectedModCount = modCount;
public boolean hasNext() {
return remaining != 0;
}
@SuppressWarnings("unchecked") public E next() {
ArrayList<E> ourList = ArrayList.this;
int rem = remaining;
if (ourList.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
if (rem == 0) {
throw new NoSuchElementException();
}
remaining = rem - 1;
return (E) ourList.array[removalIndex = ourList.size - rem];
}
public void remove() {
Object[] a = array;
int removalIdx = removalIndex;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
if (removalIdx < 0) {
throw new IllegalStateException();
}
System.arraycopy(a, removalIdx + 1, a, removalIdx, remaining);
a[--size] = null; // Prevent memory leak
removalIndex = -1;
expectedModCount = ++modCount;
}
}
如上面的PizzaMenu菜单中的createIterator()方法,其实直接可以这样使用
public class PizzaMenu implements Menu {
ArrayList menuItems;
public PizzaMenu() {
menuItems = new ArrayList();
addItem("芝士披萨", "芝士加番茄", true, 2.99);
addItem("榴莲披萨", "榴莲味", true, 3.99);
}
public void addItem(String name, String description, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
menuItems.add(menuItem);
}
public Iterator createIterator() {
return menuItems.iterator();
}
// other menu methods here
}
当然我们之前的Iterator是我们自己定义的,其实JDK也有现成的迭代器接口,我们可以直接使用,它提供了三个方法供我们实现:
public interface Iterator<E> {
public boolean hasNext();
public E next();
public void remove();
}
而且在JDK1.5之后,我们可以用for/in语法遍历迭代器,而不用再像上面那种方式去遍历数据了:
之前:
private 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());
}
}
现在:
ArrayList<MenuItem> menuItems = new ArrayList();
menuItems.add(new MenuItem());
// ……
for(MenuItem menuItem : menuItems){
System.out.print(menuItem.getName() + ", ");
System.out.print(menuItem.getPrice() + " -- ");
System.out.println(menuItem.getDescription());
}
JDK中的迭代器:
ArrayList --> ArrayListIterator
HahsMap --> KeyIterator、ValueIterator、EntryIterator
LinkedList --> LinkIterator
Vector --> SimpleListIterator
……
内部迭代器
刚才我们实现的是一个外部迭代器,什么是外部迭代器呢,其实很简单,就是客户端通过调用next()方法取得下一个元素,而内部迭代器是由迭代器内部控制一切,我们只需要告诉它要做的操作,其他的事就由它自己完成了。