设计模式学习笔记——迭代器模式

迭代器模式

顾名思义,就是将遍历等操作封装起来,对外统一成一个接口,获取遍历器,方便用户代码使用。
概念定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

小例子

例子摘自head first,帮助理解。

餐厅一

餐厅一的现有程序,使用ArrayList作为菜单的数据结构,如下:

public class PancakeHouseMenu {
    private ArrayList<MenuItem> menuItems;

    public PancakeHouseMenu() {
        menuItems = new ArrayList<>();
        addItem("pancake1", "good1", true, 10.5);
        addItem("pancake2", "good2", false, 11.5);
        addItem("pancake3", "good3", true, 12.5);
        addItem("pancake4", "good4", false, 13.5);
    }

    public void addItem(String name, String description, boolean vegetarian, double prive) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, prive);
        menuItems.add(menuItem);
    }

    public ArrayList<MenuItem> getMenuItems() {
        return menuItems;
    }
    //..其他方法

}

餐厅二

餐厅二的现有程序,使用数组作为菜单的数据结构,如下:

public class DinnerMenu {
    private final static int MAX_NUMBER_OF_ITEMS = 4;
    private MenuItem[] menuItems;
    private int numberOfItems = 0;

    public DinnerMenu() {
        menuItems = new MenuItem[MAX_NUMBER_OF_ITEMS];
        addItem("pancake1", "good1", true, 10.5);
        addItem("pancake2", "good2", false, 11.5);
        addItem("pancake3", "good3", true, 12.5);
        addItem("pancake4", "good4", false, 13.5);
    }

    public void addItem(String name, String description, boolean vegetarian, double prive) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, prive);
        if (numberOfItems >= MAX_NUMBER_OF_ITEMS) {
            throw new RuntimeException("超过最大数量");
        } else {
            menuItems[numberOfItems] = menuItem;
            numberOfItems++;
        }
    }

    public MenuItem[] getMenuItems() {
        return menuItems;
    }
    //..其他方法

}

餐厅合并,苦了服务员

餐厅合并,服务员的代码程序需要给客户展示菜单,及print所有的菜单项,这时候麻烦来了。

public class oldWaiter {
    private ArrayList<MenuItem> menuItemArrayList;
    private MenuItem[] menuItems;

    public oldWaiter() {
        PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
        menuItemArrayList = pancakeHouseMenu.getMenuItems();

        DinnerMenu dinnerMenu = new DinnerMenu();
        menuItems = dinnerMenu.getMenuItems();
    }

    public void print() {
        for (int i = 0; i < menuItemArrayList.size(); i++) {
            System.out.println(menuItemArrayList.get(i).toString());
        }
        for (int i = 0; i < menuItems.length; i++) {
            System.out.println(menuItems[i].toString());
        }
    }
}

代码好像看似还比较简单,当然我们只是举例子,但如果有3个、4个、5个餐厅呢?代码是不是变得重复和冗余,功能都是遍历和打印。

思考一下

代码变化的部分是不同的数据结构,我们是不是可以封装遍历呢,使用统一接口的方法就可以遍历任意的数据结构?

利用迭代器

我们创建一个迭代器接口:

public interface Iterator {
    boolean hasNext();

    Object next();
}

迭代器接口定义了一个hasNext()和next()方法,其中hasNext()方法判断数据结构中是否还有数据,也就是游标是否到了尽头。next()方法负责返回游标所处的位置。
实现两个餐厅菜单的迭代器:

public class PancakeHouseMenuIterator implements Iterator {
    private ArrayList<MenuItem> menuItemArrayList;
    private int iteratorIndex = 0;

    public PancakeHouseMenuIterator(ArrayList<MenuItem> menuItemArrayList) {
        this.menuItemArrayList = menuItemArrayList;
    }

    @Override
    public boolean hasNext() {
        if (iteratorIndex >= menuItemArrayList.size()) {
            return false;
        } else {
            return true;
        }
    }

    @Override
    public Object next() {
        MenuItem menuItem = menuItemArrayList.get(iteratorIndex);
        iteratorIndex++;
        return menuItem;
    }
}
public class DinnerMenuIterator implements Iterator {
    private MenuItem[] menuItems;
    private int iteratorIndex = 0;

    public DinnerMenuIterator(MenuItem[] menuItems) {
        this.menuItems = menuItems;
    }

    @Override
    public boolean hasNext() {
        if (iteratorIndex >= menuItems.length || menuItems[iteratorIndex] == null) {
            return false;
        } else {
            return true;
        }
    }

    @Override
    public Object next() {
        MenuItem menuItem = menuItems[iteratorIndex];
        iteratorIndex++;
        return menuItem;
    }
}

两个迭代器实现了Iterator接口,负责遍历。

改造餐厅

public class PancakeHouseMenu {
    private ArrayList<MenuItem> menuItems;

    public PancakeHouseMenu() {
        menuItems = new ArrayList<>();
        addItem("pancake1", "good1", true, 10.5);
        addItem("pancake2", "good2", false, 11.5);
        addItem("pancake3", "good3", true, 12.5);
        addItem("pancake4", "good4", false, 13.5);
    }

    public void addItem(String name, String description, boolean vegetarian, double prive) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, prive);
        menuItems.add(menuItem);
    }

//    public ArrayList<MenuItem> getMenuItems() {
//        return menuItems;
//    }

    public PancakeHouseMenuIterator getIterator() {
        return new PancakeHouseMenuIterator(this.menuItems);
    }
    //..其他方法
}

餐厅一主体代码未进行改动,只是注释了获取菜单的方法(该方法或暴露我们内部的结构,如List),改成了获取菜单的迭代器。

public class DinnerMenu {
    private final static int MAX_NUMBER_OF_ITEMS = 4;
    private MenuItem[] menuItems;
    private int numberOfItems = 0;

    public DinnerMenu() {
        menuItems = new MenuItem[MAX_NUMBER_OF_ITEMS];
        addItem("pancake1", "good1", true, 10.5);
        addItem("pancake2", "good2", false, 11.5);
        addItem("pancake3", "good3", true, 12.5);
        addItem("pancake4", "good4", false, 13.5);
    }

    public void addItem(String name, String description, boolean vegetarian, double prive) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, prive);
        if (numberOfItems >= MAX_NUMBER_OF_ITEMS) {
            throw new RuntimeException("超过最大数量");
        } else {
            menuItems[numberOfItems] = menuItem;
            numberOfItems++;
        }
    }

//    public MenuItem[] getMenuItems() {
//        return menuItems;
//    }

    public DinnerMenuIterator getIterator() {
        return new DinnerMenuIterator(this.menuItems);
    }
    //..其他方法
}

同样,餐厅二主体代码未进行改动,注释获取菜单的方法(该方法或暴露我们内部的结构,如数组),改成了获取菜单的迭代器。

改造完成,服务员使用新接口

由于两个餐厅使用了同一个接口,因此我们可以针对接口编程,而不是针对实现编程。

public class NewWaiter {
    private Iterator pancakeHouseMenuIterator;
    private Iterator dinnerMenuIterator;

    public NewWaiter() {
        PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
        this.pancakeHouseMenuIterator = pancakeHouseMenu.getIterator();

        DinnerMenu dinnerMenu = new DinnerMenu();
        this.dinnerMenuIterator = dinnerMenu.getIterator();
    }

    public void printMenu() {
        printItems(pancakeHouseMenuIterator);
        printItems(dinnerMenuIterator);
    }

    public void printItems(Iterator iterator) {
        while (iterator.hasNext()) {
            System.out.println(iterator.next().toString());
        }
    }
}

使用了迭代器模式,我们的女招待是不是轻松了点呢,至少服务员不负责遍历,遍历交给了迭代器完成。

目前迭代器的类图

这里写图片描述

让服务员更简洁

两个菜单都是用了相同的接口,我们需要为菜单实现一个接口,针对接口编程。
编写菜单获取迭代器的接口:

public interface Menu {
    Iterator getIterator();
}

让两个餐厅实现接口:

//实现获取迭代器的接口
public class DinnerMenu implements Menu{
    private final static int MAX_NUMBER_OF_ITEMS = 4;
    private MenuItem[] menuItems;
    private int numberOfItems = 0;

    public DinnerMenu() {
        menuItems = new MenuItem[MAX_NUMBER_OF_ITEMS];
        addItem("pancake1", "good1", true, 10.5);
        addItem("pancake2", "good2", false, 11.5);
        addItem("pancake3", "good3", true, 12.5);
        addItem("pancake4", "good4", false, 13.5);
    }

    public void addItem(String name, String description, boolean vegetarian, double prive) {
        MenuItem menuItem = new MenuItem(name, description, vegetarian, prive);
        if (numberOfItems >= MAX_NUMBER_OF_ITEMS) {
            throw new RuntimeException("超过最大数量");
        } else {
            menuItems[numberOfItems] = menuItem;
            numberOfItems++;
        }
    }

//    public MenuItem[] getMenuItems() {
//        return menuItems;
//    }
    @Override
    public DinnerMenuIterator getIterator() {
        return new DinnerMenuIterator(this.menuItems);
    }
    //..其他方法
}

另一个餐厅类似,接下在改造服务员,使之针对接口编程。

public class NewWaiter {
    private Menu pancakeHouseMenu;
    private Menu dinnerMenu;

    public NewWaiter(Menu pancakeHouseMenu, Menu dinnerMenu) {
        this.pancakeHouseMenu = pancakeHouseMenu;
        this.dinnerMenu = dinnerMenu;
    }

    public void printMenu() {
        printItems(pancakeHouseMenu.getIterator());
        printItems(dinnerMenu.getIterator());
    }

    public void printItems(Iterator iterator) {
        while (iterator.hasNext()) {
            System.out.println(iterator.next().toString());
        }
    }
}

这样编程的好处,减少服务员与具体菜单的依赖。

现在的类图

这里写图片描述

迭代器模式把在元素之间游走的责任交给迭代器,而不是聚合对象,这不仅让聚合的接口和实现变得简洁,也可以让聚合对象更专注在它所应该专注的事情上面(管理对象集合)。


设计原则

一个类应该只有一个引起变化的原因
个人理解:类应该有单一责任,负责一个单一的功能,过多的功能会引起过多的关联,超过一个责任,意味着超过一个改变的区域。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值