提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。
the iterator pattern is a design pattern in which an iterator is used to traverse a container and access the container’s elements. The iterator pattern decouples algorithms from containers; in some cases, algorithms are necessarily container-specific and thus cannot be decoupled.
在迭代器模式中,使用迭代器iterator遍历容器,并访问容器中的元素。
迭代器模式将算法与容器解耦,在一些情况下,算法只适用于特定的容器,因此不能被去耦。
两种菜单,两种容器:
// 列表
class PancakeHouseMenu :public Menu {
ArrayList menuItems;
}
// 数组
class DinerMenu : public Menu {
static final int MAX_ITEMS = 6;
int numberOfItems = 0;
MenuItem[] menuItems;
}
两种遍历方式:
// 早餐遍历
ArrayList breakfastItems = pancakeHouseMenu.getMenuItems(); // 返回ArrayList
for (int i = 0; i < breakfastItems.size(); i++) {
MenuItem menuItem = (MenuItem)breakfastItems.get(i);
}
// 午餐遍历
MenuItem[] lunchItems = dinerMenu.getMenuItems(); // getMenuItems返回数组
for (int i = 0; i < lunchItems.length; i++) {
MenuItem menuItem = lunchItems[i];
}
如果需要打印菜单,则需要调用菜单的getMenuItems()
方法,获取各自的菜单项。如果有多个菜单,则需要多个for
循环遍历。女招待根据顾客的需要打印菜单,如果这样做,女招待中的打印代码会因菜单数目而增加。需要摆脱遍历菜单的多个循环—-封装遍历。菜单也需要实现一个相同的接口。
观察两个遍历的共同部分,用迭代器封装“遍历集合内的每个对象的过程”:
Iterator breakfastIterator = pancakeHouseMenu.createIterator();
while (breakfastIterator .hasNext()) {
MenuItem menuItem = (MenuItem)breakfastIterator .next();
}
Iterator dinnerIterator = dinerMenu.createIterator();
while (dinnerIterator .hasNext()) {
MenuItem menuItem = (MenuItem)dinnerIterator .next();
}
两种迭代器子类:
// 煎饼屋迭代器
class PancakeHouseMenuIterator : public Iterator {
ArrayList items;
int position = 0;
public PancakeHouseMenuIterator(ArrayList items) {
this.items = items;
}
Object next() { ... }
bool hasNext() { ... }
};
// 晚餐迭代器
class DinerMenuIterator : public Iterator {
MenuItem[] items;
int position = 0;
public:
DinerMenuIterator(MenuItem[] items) {
this.items = items;
}
Object next() {
MenuItem menuItem = items[position];
position = position + 1;
return menuItem;
}
bool hasNext() { ... }
}
迭代器父类:
class Iterator {
virtual boolean hasNext() = 0;
virtual Object next() = 0;
}
两种类型的迭代器,继承自同一个Iterator接口,使用共同接口hasNext()和next().
防止在女招待中传入的菜单参数为PancakeHouseMenu
和DinnerMenu
,将两种菜单类继承自同一份菜单父类Menu
:
// 煎饼屋菜单
class PancakeHouseMenu : public Menu {
ArrayList menuItems;
}
// 晚餐菜单
class DinerMenu : public Menu {
MenuItem[] menuItems;
}
这样女招待的构造函数,菜单传入参数可以改为:
Waitress(Menu *pancakeHouseMenu,Menu *dinerMenu);
菜单定义:
class Menu {
public:
virtual Iterator * createIterator();
// 不用引用,是因为引用必须指向实在的对象,使用指针,迭代器指针是否真的创建成功,可以用nullptr判断
}
上面的两种菜单君继承自同一个菜单Menu
。
如果菜单项定义不同怎么办?继承 –》 指向超类的指针。 但是要求接口相同。适配器模式—-》 转换接口。
这样一来,依赖于具体菜单的女招待:
变为不依赖具体菜单的女招待:
两个菜单:PancakeHouseMenu & DinnerMenu。实现了两个相同的方法,createIterator() 用来创建迭代器。
// 煎饼屋菜单
class PancakeHouseMenu : public Menu {
ArrayList menuItems;
public:
Iterator createIterator() {
return new PancakeHouseMenuIterator(menuItems);
}
}
// 晚餐菜单
class DinerMenu : public Menu {
MenuItem[] menuItems;
public:
public Iterator createIterator() {
return new DinerMenuIterator(menuItems);
}
}
迭代器让女招待从具体类的实现中解耦。不需要知道菜单是使用数组还是列表实现。只要关心她能取得迭代器
class Waitress{
private:
PancakeHouseMenu* pancakeHouseMenu;
DinnerMenu* dinnerMenu;
public:
Waitress(Menu *pHM, Menu *dM){ // 菜单透明
pancakeHouseMenu = pHM;
dinnerMenu = dM;
}
void printMenu(){ // 取得迭代器
Iterator pHM = pancakeHouseMenu.createIterator();
Iterator dM = dinnerMenu.createIterator();
}
void printMenu(Iterator it){ // 相同遍历方式
while(it.hasNext()){
// ...
}
}
};
菜单项定义:
public class MenuItem {
String name;
String description;
boolean vegetarian;
double price;
// ...
}
也可以将不同的菜单存入一个ArrayList中。通过遍历菜单,得到每个菜单的迭代器,用迭代器对每个菜单的项进行遍历。