什么是迭代器模式
正式定义
迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而不暴露其内部的表示。
把游走的任务放在迭代器上,而不是聚合上,这样简化了聚合的接口和实现,也让责任各得其所。
一个迭代器模式的结构
- 一个聚合对象的接口,该接口中包含创建迭代器的方法,具体的聚合类实现该接口的创建迭代器方法,并返回一个迭代器对象;
- 一个迭代器接口,包含next、hasNext、remove等方法,每一个具体聚合实例化一个具体迭代器;
- 一个客户类,负责用迭代器遍历聚合对象中的集合。
下面是一个具体的例子(遍历打印不同餐厅的菜单,它们采用不同的数据结构):
一个迭代器接口:
public interface Iterator {
boolean hasNext();
Object next();
}
一个聚合对象Menu的接口:
public interface Menu {
public Iterator createIterator();
}
一个具体的菜单类,它调用创建迭代器方法来遍历内部集合,其它餐厅可以采用HashMap或ArrayList:
public class DinerMenu implements Menu{
static final int MAX_ITEMS = 6;
int numberOfItems = 0;
MenuItem[] menuItems;
public DinerMenu(){
menuItems = new MenuItem[MAX_ITEMS];
addItem("Vegetarian BLT","(Fakin') Bacon with lettuce & tomato on whole wheat",true,2.99);
addItem("BLT","Bacon with Lettuce & tomato on whole wheat",false,2.99);
addItem("Soup of the day","Soup of the day,with a side of potato salad",false,3.29);
addItem("Hotdog","A hot dog,with saurkraut,relish,onions,topped with cheese",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.err.println("Sorry, menu is full! Can't add item to menu");
}else {
menuItems[numberOfItems] = menuItem;
numberOfItems++;
}
}
/*public MenuItem[] getMenuItems() {
return menuItems;
}*/
public Iterator createIterator(){
return new DinerMenuIterator(menuItems);
}
}
对应的具体迭代器:
public class DinerMenuIterator implements Iterator {
MenuItem[] items;
int position = 0;
public DinerMenuIterator(MenuItem[] items){
this.items = items;
}
public Object next(){
MenuItem menuItem = items[position];
position++;
return menuItem;
}
@Override
public boolean hasNext() {
if(position >= items.length || items[position] == null)
return false;
else
return true;
}
}
这时候,假设客户是服务员,他要打印所有菜单,只需要用各自的迭代器遍历即可,不需要考虑聚合对象内部实现:
public class Waitress {
Menu pancakeHouseMenu;
Menu dinerMenu;
Menu cafeMenu;
public Waitress(Menu pancakeHouseMenu, Menu dinerMenu, Menu cafeMenu){
this.pancakeHouseMenu = pancakeHouseMenu;
this.dinerMenu = dinerMenu;
this.cafeMenu = cafeMenu;
}
public void printMenu(){
Iterator pancakeIterator = pancakeHouseMenu.createIterator();
Iterator dinerIterator = dinerMenu.createIterator();
Iterator cafeIterator = cafeMenu.createIterator();
System.out.println("MENU\n----\nBREAKFAST");
printMenu(pancakeIterator);
System.out.println("\nLUNCH");
printMenu(dinerIterator);
System.out.println("\nDINNER");
printMenu(cafeIterator);
}
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());
}
}
}
这是菜单项MenuItem的实现:
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;
}
}
在java中,所有集合类都实现了迭代器接口,可以直接调用。
组合模式
在上面的例子中,每有新餐厅加入,服务员就要修改printMenu的方法,这时候可以将服务员中的菜单项放在一个数组中,用数组的迭代器遍历每个菜单。但是,当一个菜单要拥有一份子菜单时,因为类型不同,所以不能用迭代器遍历了,这时候,就需要组合模式。
组合模式的定义:
组合模式允许你将对象组合成树形结构来表现 “整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
- 组合模式让我们能用树形方式创建对象的结构,树里面包含了组合以及个别的对象;
- 使用组合结构,我们能把相同的操作应用在组合和个别对象上,换句话说,在大多数情况下,我们可以忽略对象组合和个别对象之间的差异。
下面是一个例子(在一个餐厅中加入另一个菜单):
这是组合接口,包含了树所需的add、remove方法,也包含了组件需要的方法,提供默认实现:
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 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;
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return description;
}
@Override
public boolean isVegetarian() {
return vegetarian;
}
@Override
public double getPrice() {
return price;
}
@Override
public void print() {
System.out.print(" "+getName());
if(isVegetarian()){
System.out.print("(v)");
}
System.out.println(", "+getPrice());
System.out.println(" --"+getDescription());
}
菜单类是聚合类,实现组合接口:
public class Menu extends MenuComponent {
ArrayList menuComponents = new ArrayList();
String name;
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);
}
@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 iterator = menuComponents.iterator();
while (iterator.hasNext()){
MenuComponent menuComponent = (MenuComponent) iterator.next();
menuComponent.print();
}
}
}
这样,菜单类就可以添加新的菜单,形成一个树形结构。
这是客户的代码,相较迭代器更加简洁,菜单发生改变也无需修改:
public class Waitress {
MenuComponent allMenus;
public Waitress(MenuComponent allMenus){
this.allMenus = allMenus;
}
public void printMenu(){
allMenus.print();
}
}
可以像下面这样添加菜单:
MenuComponent pancakeHouseMenu = new Menu("PANCAKE HOUSE MENU","Brekfast");
MenuComponent dinerMenu = new Menu("DINER MENU","LUNCH");
MenuComponent cafeMenu = new Menu("CAFE MENU","Dinner");
MenuComponent dessertMenu = new Menu("DESSERT MENU","Dessert of course!");
这些菜单既可以添加单个的MenuItem,也可以添加新的Menu,如下所示:
dinerMenu.add(dessertMenu);
dessertMenu.add(new MenuItem("Apple Pie","Apple pie with a flakey " +
"crust,topped with vanilla ice cream",true,1.59));
组合迭代器
上面的例子是在Menu的print方法里调用迭代器,可以遍历一个菜单里的菜单项和子菜单,当服务员想要遍历整个组合时,可以可以利用组合迭代器,为所有的组合对象创建迭代器。
首先在组合接口中加入createIterator方法,在菜单中实现,返回组合迭代器,下面是组合迭代器的实现:
public class CompositeIterator implements Iterator {
Stack stack = new Stack();
public CompositeIterator(Iterator iterator){
stack.push(iterator);
}
@Override
public Object next() {
if(hasNext()){
Iterator iterator = (Iterator) stack.peek();
MenuComponent menuComponent = (MenuComponent) iterator.next();
if(menuComponent instanceof Menu){
stack.push(menuComponent.createIterator());
}
return menuComponent;
}else {
return null;
}
}
public boolean hasNext(){
if(stack.empty())
return false;
else {
Iterator iterator = (Iterator) stack.peek();
if(! iterator.hasNext()){
stack.pop();
return hasNext();
}
else
return true;
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
具体的方法是将所有组合入栈,一个个出栈,如果元素是Menu,再将其入栈,这样,就可以遍历所有组合对象了。