迭代器与组合模式

在看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方法的写法是不对的,应改成上文中的形式。可是为什么不对呢。大体上说,是因为,每当迭代到某个菜单时,菜单也会创建一个对于菜单内部整体的迭代器,由于嵌套和多态的作用,这个部分整体的迭代器会被加入到多个其他的整体迭代器中,会导致同一项被重复输出多次。所以应改为上文中的写法,不创建整体迭代器,只是返回一个部分迭代器。这样就可以保证每次只有一个迭代器被放入整体迭代器中,从而没有重复的输出。

这也表明了迭代的威力和精密,每一个能够正确运行的嵌套程序就像一个精密的仪器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值