1,定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
2,类图及角色介绍。
Aggregate:聚合接口,有一个createIterator()接口方法,所有的具体聚合类都实现这个接口并实现接口方法;
ConcreteAggregate:具体聚合类,持有一个对象的集合,实现createIterator()方法,利用此方法返回集合的具体迭代器;
Iterator:这是所有的迭代器都必须实现的接口,它包含一些方法,利用这些方法可以在集合元素之间游走;
ConcreteIterator:具体迭代器,真正负责集合的遍历。
Client:客户端。客户端只需关心聚合接口和迭代器接口,客户端持有聚合接口的引用,利用构造方法取得聚合对象,然后调用聚合对象的creatIterator()方法得到迭代器,然后调用迭代器的各个方法就可以遍历集合了,这样,客户端不需要知道具体是怎么遍历的,只知道使用那个迭代器就可以正确遍历。
3,一个小例子
(1)创建一个聚合接口(菜单接口)
/**
* 菜单接口(聚合类接口)
*/
public interface Menu {
// 创建迭代器
public Iterator createIterator();
}
(2)创建菜单项(简单Java类)
/**
* 菜单项(一个简单Java类)
*/
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 String toString() {
return (name + ", $" + price + "\n " + description);
}
}
(3)创建两个具体聚合类(餐厅菜单,煎饼屋菜单)
/**
* 餐厅菜单
*/
public class DinerMenu implements Menu {
static final int MAX_ITEMS = 6;
int numberOfItems = 0;
// 根据返回的具体迭代器的请求参数类型确定该属性的类型
// 如返回的是一个DinerMenuIterator,它需要一个数组类型的参数,那么这个属性就是数组类型
MenuItem[] menuItems;
public DinerMenu() {
menuItems = new MenuItem[MAX_ITEMS];
addItem("西红柿炒鸡蛋", "要用到西红柿,鸡蛋,糖,葱", false, 10.00);
addItem("辣椒炒肉", "要用很辣的辣椒和瘦肉炒才好吃", false, 10.00);
addItem("木耳肉丝", "用新鲜的木耳,和切成丝的肉一起炒", false, 10.00);
addItem("酸豆角炒肉", "酸豆角切成小段小段,加上瘦肉和辣椒", false, 10.00);
}
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;
}
// 返回一个具体的迭代器DinerMenuIterator
public Iterator createIterator() {
return new DinerMenuIterator(menuItems);
}
}
/**
* 煎饼屋菜单
*/
public class PancakeHouseMenu implements Menu {
// 根据返回的具体迭代器的请求参数类型确定该属性的类型
// 如返回的是一个PancakeHouseMenuIterator,它需要一个List集合类型的参数,那么这个属性就是list集合类型
ArrayList<MenuItem> menuItems;
// 构造方法加载菜单项,供迭代器进行遍历
public PancakeHouseMenu() {
menuItems = new ArrayList<MenuItem>();
addItem("葱花饼", "好吃", false, 2.99);
addItem("酱香饼", "不贵", false, 2.99);
addItem("酸菜饼", "物美", false, 3.49);
addItem("鸡蛋饼", "价廉", false, 3.59);
}
public void addItem(String name, String description, boolean vegetarian,double price){
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
menuItems.add(menuItem);
}
public ArrayList<MenuItem> getMenuItems() {
return menuItems;
}
// 该方法返回一个煎饼屋菜单迭代器
public Iterator createIterator() {
return new PancakeHouseMenuIterator(menuItems);
}
public String toString() {
return "Objectville Pancake House Menu";
}
}
(4)创建一个迭代器接口
/**
* 迭代器接口
*/
public interface Iterator {
// 是否还有下一个
boolean hasNext();
// 返回菜单项
MenuItem next();
}
(5)创建两个具体迭代器(餐厅菜单迭代器,煎饼屋菜单迭代器)
/**
* 餐厅菜单迭代器
*/
public class DinerMenuIterator implements Iterator {
MenuItem[] items;
int position = 0;
public DinerMenuIterator(MenuItem[] items) {
this.items = items;
}
public MenuItem 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 PancakeHouseMenuIterator implements Iterator {
ArrayList<MenuItem> items;
int position = 0;
public PancakeHouseMenuIterator(ArrayList<MenuItem> items) {
this.items = items;
}
// 迭代器方法,遍历集合中的元素
public MenuItem next() {
MenuItem item = items.get(position);
position = position + 1;
return item;
}
// 迭代器方法,判断是否还有下一个元素
public boolean hasNext() {
if (position >= items.size()) {
return false;
} else {
return true;
}
}
}
(6)创建客户端(一个女招待)
/**
* 女招待(客户端)(只需关心菜单接口和迭代器接口。其实感觉女招待只要和
* 菜单接口打交道就行了,因为传入什么菜单,也就确定了用什么迭代器)
*/
public class Waitress {
// 客户端持有各个菜单的引用
Menu pancakeHouseMenu;
Menu dinerMenu;
// 通过构造方法获取需要的菜单(什么菜单都行,只要实现了菜单接口,这样女招待
// 就不需要知道菜单的具体实现了,成功解耦)
public Waitress(Menu pancakeHouseMenu, Menu dinerMenu) {
this.pancakeHouseMenu = pancakeHouseMenu;
this.dinerMenu = dinerMenu;
}
// 打印菜单
public void printMenu() {
// 通过传入的菜单获得相应的迭代器(具体是什么迭代器不知道,只知道实现了
// 菜单接口的接口方法createIterator()要返回一个迭代器的超类)
Iterator pancakeIterator = pancakeHouseMenu.createIterator();
Iterator dinerIterator = dinerMenu.createIterator();
// 打印菜单
System.out.println("煎饼屋菜单:");
printMenu(pancakeIterator);
System.out.println("餐厅菜单:");
printMenu(dinerIterator);
}
// 打印菜单
private void printMenu(Iterator iterator) {
while (iterator.hasNext()) {
MenuItem menuItem = iterator.next();
System.out.print(menuItem.getName() + ", ");
System.out.print(menuItem.getPrice() + " -- ");
System.out.println(menuItem.getDescription());
}
}
}
(7)创建测试类
public class MenuTestDrive {
public static void main(String args[]) {
// 创建两个菜单
PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
DinerMenu dinerMenu = new DinerMenu();
// 创建一个女招待
Waitress waitress = new Waitress(pancakeHouseMenu, dinerMenu);
// 女招待打印菜单
waitress.printMenu();
}
}
(8)输出
煎饼屋菜单:
葱花饼, 2.99 -- 好吃
酱香饼, 2.99 -- 不贵
酸菜饼, 3.49 -- 物美
鸡蛋饼, 3.59 -- 价廉
餐厅菜单:
西红柿炒鸡蛋, 2.99 -- 要用到西红柿,鸡蛋,糖,葱
辣椒炒肉, 2.99 -- 要用很辣的辣椒和瘦肉炒才好吃
木耳肉丝, 3.29 -- 用新鲜的木耳,和切成丝的肉一起炒
酸豆角炒肉, 3.05 -- 酸豆角切成小段小段,加上瘦肉和辣椒
4,小结
迭代器作用:如果你有一个统一的方法访问聚合中的每一个对象,你就可以编写多态的代码和这些聚合搭配使用。如同例子中的printMenu()方法一样,只要有了迭代器,这个方法根本不管菜单项是由数组还是由ArrayList来保存的。
迭代器模式把在元素之间游走的责任交给迭代器,而不是聚合对象。这不仅让聚合的接口和实现变得简洁,也可以让聚合更专注在它所应该专注的事情上面(也就是管理对象集合),而不必去理会遍历的事情。
这也很容易扩展,如果要加入一个新的菜单,而这个菜单是由另一个数据结构保存的,那么我们也只是再写一个遍历该数据结构的迭代器,且该迭代器实现迭代器接口,然后让新菜单实现菜单接口,再修改一下女招待的构造方法就行了。