2008-02-29
设计模式入门学习之迭代器与组合模式(3)
关键字: 设计模式接上篇
在我们新的设计中,真正需要做些什么呢?
1.我们需要某种树形结构,可以容纳菜单、子菜单、菜单项
2.我们需要确定那个在每个菜单的各个项之间游走,而且至少要像在用迭代器一样方便
3.我们要能够更有弹性的在菜单项之间游走,比如说可能只需要遍历甜点菜单,或者可以遍历餐厅的整个菜单
管理菜单的问题已经到了一个迭代器无法解决问题的新维度,我们改用“组合模式”来实现这部分
组合模式允许你讲对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合
使用组合结构,我们能把相同的操作应用在组合和个别对象上。换句话说,在大多数情况下,我们可以忽略对象组合和个别对象之间的差别。
我们要如何在菜单上应用组合模式呢?一开始,我们需要创建一个组件接口来作为菜单和菜单项的共同接口,让我们能够用统一的做法来处理菜单和菜单项。换句话说,我们可以针对菜单或菜单项调用相同的方法。
//实现菜单组件的抽象类,请记住,菜单组件的角色是为夜节点和组合节点提供一个共同接口
public abstract class MenuComponent {
//我们把“组合”方法组织在一起,包括新增、删除、取得菜单组件
public void add(MenuComponent menuComponent) {
//默认实现是抛出UnsupportedOperationException异常,
//这样如果菜单或菜单项不支持某个操作,他们就不需要做任何事情
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();
}
//print方法同时被菜单和菜单项所实现
public void print() {
throw new UnsupportedOperationException();
}
}
//实现菜单项,首先扩展MenuComponent接口
public class MenuItem extends MenuComponent {
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 void print() {
System.out.print(" " + getName());
if (isVegetarian()) {
System.out.print("(v)");
}
System.out.println(", " + getPrice());
System.out.println(" -- " + getDescription());
}
}
//实现组合菜单,和菜单项一样,继承MenuComponent
public class Menu extends MenuComponent {
//菜单可以有任意数目的子菜单,这些子菜单都必须属于MenuComponent类型
ArrayList menuComponents = new ArrayList();
String name;
String description;
//和以前也不一样,我们给每个菜单一个名字和描述,之前每个菜单的类名称就是此菜单的名字
public Menu(String name, String description) {
this.name = name;
this.description = description;
}
//这里将菜单项和其他菜单加入到菜单中,因为菜单和菜单项都是menuComponent
//所以我们只需要一个方法就可以两者兼顾,remove和getChild同理
public void add(MenuComponent menuComponent) {
menuComponents.add(menuComponent);
}
public void remove(MenuComponent menuComponent) {
menuComponents.remove(menuComponent);
}
public MenuComponent getChild(int i) {
return (MenuComponent)menuComponents.get(i);
}
public String getName() {return name;}
public String getDescription() {return description;}
//能打印菜单本身的信息,也打印出菜单内所有组件的内容
public void print() {
System.out.print("\n" + getName());
System.out.println(", " + getDescription());
System.out.println("---------------------");
//看,我们用了迭代器,用它遍历所有菜单组件,遍历过程中,可能遇到其他菜单,
//或者是菜单项,会开始另一个遍历,依次类推
Iterator iterator = menuComponents.iterator();
while (iterator.hasNext()) {
MenuComponent menuComponent =
(MenuComponent)iterator.next();
menuComponent.print();
}
}
}
//java版的服务员代码变的很简单,我们只要将最顶层的菜单组件交给它就可以了
import java.util.Iterator;
public class Waitress {
MenuComponent allMenus;//最顶层的菜单
public Waitress(MenuComponent allMenus) {
this.allMenus = allMenus;
}
//她只需要调用最顶层菜单的print,就可以打印整个菜单层次,包括所有的菜单和菜单项
public void printMenu() {
allMenus.print();
}
}
//现在就是测试了
public class MenuTestDrive {
public static void main(String args[]) {
//先创建所有的菜单对象
MenuComponent pancakeHouseMenu = new Menu("PANCAKE HOUSE MENU", "Breakfast");
MenuComponent dinerMenu = new Menu("DINER MENU", "Lunch");
MenuComponent cafeMenu = new Menu("CAFE MENU", "Dinner");
MenuComponent dessertMenu = new Menu("DESSERT MENU", "Dessert of course!");
MenuComponent coffeeMenu = new Menu("COFFEE MENU", "Stuff to go ");
//我们需要一个最顶层的菜单allMenus
MenuComponent allMenus = new Menu("ALL MENUS", "All menus combined");
//使用组合的add方法,将菜单加入到顶层菜单中
allMenus.add(pancakeHouseMenu);
allMenus.add(dinerMenu);
allMenus.add(cafeMenu);
pancakeHouseMenu.add(new MenuItem(
"K&B's Pancake Breakfast",
"Pancakes with scrambled eggs, and toast",
true,
2.99));
dinerMenu.add(new MenuItem(
"Vegetarian BLT",
"(Fakin') Bacon with lettuce & tomato on whole wheat",
true,
2.99));
dinerMenu.add(new MenuItem(
"Hotdog",
"A hot dog, with saurkraut, relish, onions, topped with cheese",
false,
3.05));
dinerMenu.add(dessertMenu); //菜单中加入另一个菜单
dessertMenu.add(new MenuItem(
"Apple Pie",
"Apple pie with a flakey crust, topped with vanilla icecream",
true,
1.59));
cafeMenu.add(new MenuItem(
"Veggie Burger and Air Fries",
"Veggie burger on a whole wheat bun, lettuce, tomato, and fries",
true,
3.99));
cafeMenu.add(coffeeMenu);
coffeeMenu.add(new MenuItem(
"Coffee Cake",
"Crumbly cake topped with cinnamon and walnuts",
true,
1.59));
//一旦我们将整个菜单层次构造完毕把它交给服务员,
//你会发现服务员要将整份菜单打印出来很容易
Waitress waitress = new Waitress(allMenus);
waitress.printMenu();
}
}现在,服务员想要游走整个菜单,挑出所有的素食项,我们可能想到实现一个组合迭代器,让我们为每个组件都加上createIterator()方法,从抽象的MenuComponent类下手:
public abstract class MenuComponent {
//每个菜单和菜单项都需要实现这个方法
public void createIterator() {
throw new UnsupportedOperationException();
}
//other
}
//菜单修改
public class Menu extends MenuComponent {
//这里使用一个新的CompositeIterator迭代器,这个迭代器知道如何遍历任何组合
public Iterator createIterator() {
return new CompositeIterator(menuComponents.iterator());
}
//其他部分不需要修改
}
//菜单项修改
public class MenuItem extends MenuComponent {
public Iterator createIterator() {
//噢噢,这里怎么是个空迭代器?
return new NullIterator();
}
//其他部分不需要修改
}
/**
*空迭代器,这么说好了,菜单项内没什么可以遍历的,那么如何实现菜单项的createIterator呢?
*有2个选择:1.返回null,这么做就需要条件语句来判断返回值是否为null
* 2.返回一个迭代器,而这个迭代器的hasNext永远返回false,这个方式应该是比较好的
*/
import java.util.Iterator;
public class NullIterator implements Iterator {
//基本什么事情都不做的迭代器
public Object next() {
return null;
}
public boolean hasNext() {
return false;
}
public void remove() {//空迭代器当然不支持remove
throw new UnsupportedOperationException();
}
}
//最复杂的组合迭代器
public class CompositeIterator implements Iterator {
Stack stack = new Stack();
//将我们要遍历的顶层组合的迭代器传入,我们把它抛进一个堆栈数据结构中
public CompositeIterator(Iterator iterator) {
stack.push(iterator);
}
//好了,当客户想要取得下一个元素时,我们先调用hasNext来确定是否还有下一个
public Object next() {
if (hasNext()) {
Iterator iterator = (Iterator) stack.peek();
//如果还有下一个元素,我们就从堆栈中取出目前的迭代器,然后取得它的下一个元素
MenuComponent component = (MenuComponent) iterator.next();
if (component instanceof Menu) {
//如果元素是一个菜单,我们有了另一个需要被包含进遍历的组合,
//所以我们将它丢进堆栈中,不管是不是菜单,我们都返回改组件
stack.push(component.createIterator());
}
return component;
} else {
return null;
}
}
public boolean hasNext() {
//想要知道是否还有下一个元素,我们检查堆栈是否被清空,空了就表示没有下一个元素
if (stack.empty()) {
return false;
} else {
//否则,我们就从堆栈的顶层中取出迭代器,看是否还有下个元素
//如果它没有元素,我们将它弹出堆栈,然后递归调用hasNext()
Iterator iterator = (Iterator) stack.peek();
if (!iterator.hasNext()) {
stack.pop();
return hasNext();
} else {
return true;//否则表示还有下个元素,返回true
}
}
}
//不支持romove,只要遍历
public void remove() {
throw new UnsupportedOperationException();
}
}
//现在有了这个方法遍历菜单的每个项,我们给服务员加上一个可以确切告诉耳麦哪些项目是素食的方法
import java.util.Iterator;
public class Waitress {
MenuComponent allMenus;
public Waitress(MenuComponent allMenus) {
this.allMenus = allMenus;
}
public void printMenu() {
allMenus.print();
}
//printVegetarianMenu取得allMenus的组合并得到他的迭代器来作为我们的CompositeIterator
public void printVegetarianMenu() {
Iterator iterator = allMenus.createIterator();
System.out.println("\nVEGETARIAN MENU\n----");
while (iterator.hasNext()) {
MenuComponent menuComponent = (MenuComponent)iterator.next();
//我们在菜单上实现isVegetarian方法,让它永远抛出异常
//如果异常发生,我们就扑捉这个异常,然后继续遍历
try {
//调用每个元素的isVegetarian,如果true就调用它的print方法
if (menuComponent.isVegetarian()) {
menuComponent.print();
}
} catch (UnsupportedOperationException e) {
//只有菜单项的print方法可以被调用,绝对不能调用菜单的print方法
}
}
}
}
发表评论
- 浏览: 20846 次
- 性别:

- 来自: 北京

- 详细资料
搜索本博客
我的相册
P1080256
共 30 张
共 30 张
最近加入圈子
最新评论
-
使用iBatis的自动化代码生 ...
有没有根据类生产数据库和xml的?
-- by aninfeel -
使用iBatis的自动化代码生 ...
IBatis去除注释版 http://hugh-lin.javaeye.com/ ...
-- by hugh-lin -
使用iBatis的自动化代码生 ...
我的不能用啊,郁闷掉了,真的是很黄很暴力。
-- by horror -
搭建Cloud Computing测试 ...
to fredzhang : 呵呵,我只是说用hadoop/hbase来搭 ...
-- by blank -
搭建Cloud Computing测试 ...
1. gfs的论文你仔细阅读过了吗?它的思想决定了基于它应当如何来实现,如何才能 ...
-- by fredzhang






评论排行榜