《Head First 设计模式》:迭代器模式

本贴最后更新于 1536 天前,其中的信息可能已经沧海桑田

正文

一、定义

迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

要点:

  • 迭代器模式把在元素之间游走的责任交给迭代器,而不是聚合对象。这样简化了聚合的接口和实现,也让责任各得其所。

二、实现步骤

1、创建迭代器接口

/**
 * 迭代器接口
 */
public interface Iterator {

    /**
     * 是否有下一个元素
     */
    public boolean hasNext();
    
    /**
     * 获取下一个元素
     */
    public Object next();
}

2、创建具体迭代器,并实现迭代器接口

具体迭代器负责遍历元素,以及管理目前遍历的位置。

/**
 * 具体迭代器
 */
public class ConcreteIterator implements Iterator {
    
    /**
     * 要遍历的集合(数组)
     */
    public String[] items;
    /**
     * 当前遍历位置
     */
    int position = 0;
    
    public ConcreteIterator(String[] items) {
        this.items = items;
    }

    @Override
    public boolean hasNext() {
        if (position < items.length) {
            return true;
        }
        return false;
    }

    @Override
    public Object next() {
        if (!this.hasNext()) {
            return null;
        }
        String item = items[position];
        position = position + 1;
        return item;
    }
}

3、创建聚合接口,并定义返回迭代器的方法

/**
 * 聚合接口
 */
public interface Aggregate {

    /**
     * 创建迭代器
     */
    public Iterator createIterator();
}

4、创建具体聚合,并实现返回迭代器的方法

具体聚合里面持有集合。这里的集合指的是一群对象,其存储方式可以是列表、数组、散列表等。

/**
 * 具体聚合
 */
public class ConcreteAggregate implements Aggregate {
    
    /**
     * 持有集合(比如列表、数组、散列表等)
     */
    public String[] items = new String[] {"item1", "item2", "item3"};

    @Override
    public Iterator createIterator() {
        return new ConcreteIterator(items);
    }
}

5、使用迭代器进行遍历

public class Test {
    
    public static void main(String[] args) {
        // 聚合对象
        Aggregate aggregate = new ConcreteAggregate();
        // 迭代器
        Iterator iterator = aggregate.createIterator();
        // 遍历
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

三、举个栗子

1、背景

对象村餐厅和对象村煎饼屋合并了,现在我们可以在同一个地方,享用煎饼屋美味的煎饼早餐和好吃的餐厅午餐了。

假设你被他们合组的新公司雇佣,打算创建一个 Java 版本的女招待。这个 Java 版本的女招待规格是:能应对顾客的需要打印定制的菜单,而无需询问厨师。

现在,有一点小麻烦:煎饼屋菜单使用 ArrayList 记录菜单项,而餐厅则是使用数组记录菜单项。两者都不愿意改变他们的实现,毕竟有太多代码依赖于它们了。

好消息是,煎饼屋和餐厅都同意实现一个统一的菜单项 MenuItem。

2、实现

将集合的遍历交给迭代器,这样就不用关心菜单是使用 ArrayList 还是数组记录菜单项了。

(1)创建菜单项

/**
 * 菜单项
 */
public class MenuItem {

    String name;
    double price;
    
    public MenuItem(String name, double price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }
}

(2)创建迭代器接口

/**
 * 迭代器接口
 */
public interface Iterator {

    /**
     * 是否有下一个元素
     */
    public boolean hasNext();
    
    /**
     * 获取下一个元素
     */
    public Object next();
}

(3)创建具体的菜单迭代器

/**
 * 煎饼屋菜单迭代器
 */
public class PancakeHouseMenuIterator implements Iterator {
    
    /**
     * 列表形式的菜单项
     */
    public ArrayList<MenuItem> menuItems;
    int position = 0;
    
    public PancakeHouseMenuIterator(ArrayList<MenuItem> items) {
        this.menuItems = items;
    }

    @Override
    public boolean hasNext() {
        if (position < menuItems.size()) {
            return true;
        }
        return false;
    }

    @Override
    public Object next() {
        if (!this.hasNext()) {
            return null;
        }
        MenuItem item = menuItems.get(position);
        position = position + 1;
        return item;
    }
}
/**
 * 餐厅菜单迭代器
 */
public class DinerMenuIterator implements Iterator {
    
    /**
     * 数组形式的菜单项
     */
    public MenuItem[] menuItems;
    int position = 0;
    
    public DinerMenuIterator(MenuItem[] items) {
        this.menuItems = items;
    }

    @Override
    public boolean hasNext() {
        if (position < menuItems.length) {
            return true;
        }
        return false;
    }

    @Override
    public Object next() {
        if (!this.hasNext()) {
            return null;
        }
        MenuItem item = menuItems[position];
        position = position + 1;
        return item;
    }
}

(4)创建菜单接口

/**
 * 菜单接口
 */
public interface Menu {

    /**
     * 创建迭代器
     */
    public Iterator createIterator();
}

(5)创建具体的菜单

/**
 * 煎饼屋菜单
 */
public class PancakeHouseMenu implements Menu {
    
    public ArrayList<MenuItem> menuItems;
    
    public PancakeHouseMenu() {
        menuItems = new ArrayList<MenuItem>();
        menuItems.add(new MenuItem("Regular Pancake Breakfast", 2.99));
        menuItems.add(new MenuItem("Blueberry Pancakes", 3.49));
        menuItems.add(new MenuItem("Waffles", 3.59));
    }

    @Override
    public Iterator createIterator() {
        return new PancakeHouseMenuIterator(menuItems);
    }
}
/**
 * 餐厅菜单
 */
public class DinerMenu implements Menu {
    
    public MenuItem[] menuItems;
    
    public DinerMenu() {
        menuItems = new MenuItem[3];
        menuItems[0] = new MenuItem("BLT", 2.99);
        menuItems[1] = new MenuItem("Soup of the day", 3.29);
        menuItems[2] = new MenuItem("Hotdog", 3.05);
    }

    @Override
    public Iterator createIterator() {
        return new DinerMenuIterator(menuItems);
    }
}

(6)创建女招待

/**
 * 女招待
 */
public class Waitress {
    
    Menu pancakeHouseMenu;
    Menu dinerMenu;
    
    public Waitress(Menu pancakeHouseMenu, Menu dinerMenu) {
        this.pancakeHouseMenu = pancakeHouseMenu;
        this.dinerMenu = dinerMenu;
    }
    
    public void printMenu() {
        // 获取菜单迭代器
        Iterator pancakeIterator = pancakeHouseMenu.createIterator();
        Iterator dinerIterator = dinerMenu.createIterator();
        // 使用迭代器打印菜单
        System.out.println("----MENU----\n");
        System.out.println("BREAKFASE:");
        printMenu(pancakeIterator);
        System.out.println("\nLUNCH:");
        printMenu(dinerIterator);
    }
    
    private void printMenu(Iterator iterator) {
        while (iterator.hasNext()) {
            MenuItem menuItem = (MenuItem)iterator.next();
            System.out.println(menuItem.getName() + ", " + menuItem.getPrice());
        }
    }
}

(7)使用女招待打印菜单

public class Test {
    
    public static void main(String[] args) {
        // 菜单
        Menu pancakeHouseMenu = new PancakeHouseMenu();
        Menu dinerMenu = new DinerMenu();
        // 女招待
        Waitress waitress = new Waitress(pancakeHouseMenu, dinerMenu);
        // 打印菜单
        waitress.printMenu();
    }
}
  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    200 引用 • 120 回帖
1 操作
jingqueyimu 在 2020-09-07 22:05:37 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...