迭代器模式iterator pattern

提供一种方法访问一个容器(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().

防止在女招待中传入的菜单参数为PancakeHouseMenuDinnerMenu,将两种菜单类继承自同一份菜单父类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

如果菜单项定义不同怎么办?继承 –》 指向超类的指针。 但是要求接口相同。适配器模式—-》 转换接口。

这样一来,依赖于具体菜单的女招待:

迭代器模式iterator pattern_第1张图片

变为不依赖具体菜单的女招待:

迭代器模式iterator pattern_第2张图片
两个菜单: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中。通过遍历菜单,得到每个菜单的迭代器,用迭代器对每个菜单的项进行遍历。

你可能感兴趣的:(设计模式)