在看Headfirst 设计模式的迭代器与组合模式这一章里,发现书中的错误在此记录。
总体思路:
将组合模式和迭代器模式结合起来,可以得到一个可以便利整个组合的“外部迭代器”。
首先假设每个具体的组合可以分为菜单和菜单项。首先抽象出一个表示组合的父类
package Pattern.zuhe;
import java.util.Iterator;
public abstract class MenuComponent {
public void add(MenuComponent menuComponent){
throw new UnsupportedOperationException();
}
public void remove(MenuComponent menuComponent){
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 Iterator<MenuComponent> createIterator() {
throw new UnsupportedOperationException();
}
}
分别实现菜单和菜单项类
package Pattern.zuhe;
import java.util.ArrayList;
import java.util.Iterator;
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 menuComponent){
menuComponents.add(menuComponent);
}
@Override
public void remove(MenuComponent menuComponent){
menuComponents.remove(menuComponent); //ArrayList的remove方法基于equals,所以参数要求为Object
}
@Override
public MenuComponent getChild(int i){
return (MenuComponent)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();
// }
}
@Override
public Iterator<MenuComponent> createIterator() {
//return new CompositeIterator(menuComponents.iterator()); 书中这里是这么写的,CompositeIterator类的实现在下面
return menuComponents.iterator();
}
}
package Pattern.zuhe;
import java.util.Iterator;
public class MenuItem extends MenuComponent{
private String name;
private String description;
private boolean isVegetarian;
private double price;
public MenuItem(String name, String description, boolean isVegetarian, double price){
this.name = name;
this.description = description;
this.isVegetarian = isVegetarian;
this.price = price;
}
@Override
public String getName(){
return name;
}
@Override
public String getDescription(){
return description;
}
@Override
public double getPrice(){
return price;
}
@Override
public boolean isVegetarian(){
return isVegetarian;
}
@Override
public void print(){
System.out.print(" " + getName());
if(isVegetarian()){
System.out.print("(v)");
}
System.out.println("," + getPrice());
System.out.println(" --" + getDescription());
}
@Override
public Iterator<MenuComponent> createIterator() {
//return new CompositeIterator(menuComponents.iterator());
return new NullIterator();
}
}
最后实现一个整体的迭代器
package Pattern.zuhe;
import java.util.Iterator;
import java.util.Stack;
public class CompositeIterator implements Iterator<MenuComponent>{
Stack<Iterator<MenuComponent>> stack = new Stack<>();
public CompositeIterator(Iterator<MenuComponent> iterator){
stack.push(iterator);
}
public MenuComponent next(){
if(hasNext()){
Iterator<MenuComponent> iterator = stack.peek();
MenuComponent component = iterator.next();
if(component instanceof Menu){
stack.push(component.createIterator());
}
return component;
}else{
return null;
}
}
public boolean hasNext(){
if(stack.empty()){
return false;
}else{
Iterator<MenuComponent> iterator = stack.peek();
if(!iterator.hasNext()){
stack.pop();
return hasNext();
}else{
return true;
}
}
}
public void remove(){
throw new UnsupportedOperationException();
}
}
创建测试类测试
package Pattern.zuhe;
//关联Menu的print方法 加上注释
public class otherClient {
public static void main(String[] args) {
MenuComponent pancakeHouseMenu = new Menu("PANCAKE HOUSE MENU", "Breakfast");
MenuComponent dinerMenu = new Menu("DINER MENU", "lunch");//diner 路边摊
MenuComponent cafeMenu = new Menu("CAFE MENU", "dinner");
MenuComponent dessertMenu = new Menu("DESSERT MENU", "dessert");
MenuComponent allMenu = new Menu("ALL MENUS", "all menus combined");
allMenu.add(pancakeHouseMenu);
allMenu.add(dinerMenu);
allMenu.add(cafeMenu);
pancakeHouseMenu.add(new MenuItem("煎饼果子", "脆脆的", true, 10));
pancakeHouseMenu.add(new MenuItem("胡辣汤", "好想吃", false, 5));
pancakeHouseMenu.add(new MenuItem("韭菜盒子", "一般般", true, 5));
dinerMenu.add(new MenuItem("红烧肉", "有肥有瘦", false, 30));
dinerMenu.add(new MenuItem("红焖大虾", "巨大的虾", false, 60));
dinerMenu.add(dessertMenu);
dessertMenu.add(new MenuItem("冰淇淋", "冰冰凉", true, 5));
dessertMenu.add(new MenuItem("奶茶", "热乎乎", true, 5));
cafeMenu.add(new MenuItem("摩卡", "不知道啥味", true, 15));
cafeMenu.add(new MenuItem("卡布奇诺", "不知道啥味", true, 15));
CompositeIterator iterator = new CompositeIterator(allMenu.createIterator());
while(iterator.hasNext()){
MenuComponent component = iterator.next();
component.print();
}
}
}
经过测试,我们发现书中的Menu类的createIterator方法的写法是不对的,应改成上文中的形式。可是为什么不对呢。大体上说,是因为,每当迭代到某个菜单时,菜单也会创建一个对于菜单内部整体的迭代器,由于嵌套和多态的作用,这个部分整体的迭代器会被加入到多个其他的整体迭代器中,会导致同一项被重复输出多次。所以应改为上文中的写法,不创建整体迭代器,只是返回一个部分迭代器。这样就可以保证每次只有一个迭代器被放入整体迭代器中,从而没有重复的输出。
这也表明了迭代的威力和精密,每一个能够正确运行的嵌套程序就像一个精密的仪器。