zoukankan      html  css  js  c++  java
  • 迭代器模式

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

          有过Java编程经验的人对这种模式应该比较熟悉,因为Java内置的许多集合类型:List、Set、Map等都提供了迭代器接口,可以使用统一的方式遍历集合中的元素。下面将通过一个例子说明迭代器的使用场景,并了解一下迭代器模式的原理。

          包子店卖的有包子和饮品,对于包子和饮品的每一个条目,我们用Item来表示,Item只包含name和price两个字段:

     1 public class Item {
     2   private String name;
     3   private double price;
     4 
     5   public Item(String name, double price){
     6     this.name = name;
     7     this.price = price;
     8   }
     9 
    10   // getters and setters ...
    11 }

          由于包子会不定期更新,所以用一个ArrayList来存储目前所有的包子类别:

     1 public class Bun {
     2   ArrayList<Item> buns;
     3 
     4   public Bun(){
     5     buns = new ArrayList<>();
     6 
     7     addBun("鲜肉包子", 1.5);
     8     addBun("香菇青菜包子",  1);
     9     addBun("鱼香肉丝包子",  1.5);
    10   }
    11 
    12   private void addBun(String name, double price){
    13     Item item = new Item(name, price);
    14     buns.add(item);
    15   }
    16 
    17   public ArrayList<Item> getBuns(){
    18     return buns;
    19   }
    20 }

          而饮品则比较固定,一般不会增加新的类型,所以就假设固定成5种好了,对于这种需求,或许我们会选择使用数组来实现:

     1 public class Drink {
     2   Item[] drinks;
     3   int position;
     4   private static final int MAX_SIZE = 5;
     5 
     6   public Drink(){
     7     drinks = new Item[MAX_SIZE];
     8     position = 0;
     9 
    10     addDrink("豆浆", 2);
    11     addDrink("八宝粥", 2);
    12     addDrink("牛奶", 2.5);
    13     addDrink("银耳汤", 3);
    14     addDrink("豆腐脑", 2);
    15   }
    16 
    17   private void addDrink(String name, double price){
    18     Item item = new Item(name, price);
    19     if(position >= MAX_SIZE){
    20       System.err.println("饮品已经满了。。。");
    21     }else{
    22       drinks[position++] = item;
    23     }
    24   }
    25 
    26   public Item[] getDrinks(){
    27     return drinks;
    28   }
    29 }

          那么,当我们需要输出早餐店里的所有包子和饮品的时候,需要怎么写呢?我们发现包子和饮品的底层存储不一样,或许都改成ArrayList会简单很多,但是由于代码已经写好了,其他很多地方都会使用上面的代码,所以冒险修改不是一个好选择,只能麻烦一些,针对两种情况分别处理:

     1   public void printItems(){
     2     Bun bun = new Bun();
     3     Drink drink = new Drink();
     4     ArrayList<Item> buns = bun.getBuns();
     5     Item[] drinks = drink.getDrinks();
     6 
     7     //输出包子
     8     for(int i=0; i<buns.size(); i++){
     9       Item item = buns.get(i);
    10       System.out.println(item.getName() + ", " + item.getPrice());
    11     }
    12 
    13     //输出饮品
    14     for(int i=0; i<drinks.length; i++){
    15       System.out.println(drinks[i].getName() + ", " + drinks[i].getPrice());
    16     }
    17   }

          输出如下:

    鲜肉包子, 1.5
    香菇青菜包子, 1.0
    鱼香肉丝包子, 1.5
    豆浆, 2.0
    八宝粥, 2.0
    牛奶, 2.5
    银耳汤, 3.0
    豆腐脑, 2.0

          这里的打印逻辑的实现有几个问题:①打印方法需要知道包子和饮品的底层实现细节,这不满足封装的要求;②打印的逻辑不能扩展,如果包子店增加了馒头类型,而某位程序员打算使用Set来存储所有的馒头,那么打印方法必须要同步修改。因此,我们要做的就是隐藏底层的逻辑,对外提供统一的遍历接口,不管底层采用什么实现,对外保持一致就行。

          为了保持遍历接口的简单性,我们不打算加入太多的逻辑,具体做法是定义一个迭代器接口,包含next()和hasNext()两个方法:

    1 public interface Iterator {
    2   boolean hasNext();
    3   Item next();
    4 }

          为包子和饮品分别定义对应的迭代器:

     1 public class BunIterator implements Iterator{
     2   ArrayList<Item> items;
     3   int position;
     4 
     5   public BunIterator(ArrayList<Item> items){
     6     this.items = items;
     7     position = 0;
     8   }
     9 
    10   @Override
    11   public boolean hasNext() {
    12     if(items == null || position >= items.size()){
    13       return false;
    14     }else{
    15       return true;
    16     }
    17   }
    18 
    19   @Override
    20   public Item next() {
    21     return items.get(position++);
    22   }
    23 
    24 
    25 public class DrinkIterator implements Iterator{
    26   Item[] items;
    27   int position;
    28 
    29   public DrinkIterator(Item[] items){
    30     this.items = items;
    31     position = 0;
    32   }
    33 
    34   @Override
    35   public boolean hasNext() {
    36     if(position >= items.length || items[position] == null){
    37       return false;
    38     }else{
    39       return true;
    40     }
    41   }
    42 
    43   @Override
    44   public Item next() {
    45     return items[position++];
    46   }
    47 }

          修改包子和饮品类,只对外提供creatorIterator方法:

     1 public class Bun{
     2   ArrayList<Item> buns;
     3 
     4   public Bun(){
     5     buns = new ArrayList<>();
     6 
     7     addBun("鲜肉包子", 1.5);
     8     addBun("香菇青菜包子",  1);
     9     addBun("鱼香肉丝包子",  1.5);
    10   }
    11 
    12   private void addBun(String name, double price){
    13     Item item = new Item(name, price);
    14     buns.add(item);
    15   }
    16 
    17   public Iterator creatorIterator(){
    18     return new BunIterator(buns);
    19   }
    20 }
    21 
    22 
    23 public class Drink {
    24   Item[] drinks;
    25   int position;
    26   private static final int MAX_SIZE = 5;
    27 
    28   public Drink(){
    29     drinks = new Item[MAX_SIZE];
    30     position = 0;
    31 
    32     addDrink("豆浆", 2);
    33     addDrink("八宝粥", 2);
    34     addDrink("牛奶", 2.5);
    35     addDrink("银耳汤", 3);
    36     addDrink("豆腐脑", 2);
    37   }
    38 
    39   private void addDrink(String name, double price){
    40     Item item = new Item(name, price);
    41     if(position >= MAX_SIZE){
    42       System.err.println("饮品已经满了。。。");
    43     }else{
    44       drinks[position++] = item;
    45     }
    46   }
    47 
    48   public Iterator creatorIterator(){
    49     return new DrinkIterator(drinks);
    50   }
    51 }

          接下来使用迭代器写一个新的打印方法:

     1 public class TestIterator {
     2 
     3   public static void main(String[] args){
     4     TestIterator test = new TestIterator();
     5     test.printItemsWithIterator();
     6   }
     7 
     8   public void printItemsWithIterator(){
     9     Bun bun = new Bun();
    10     Drink drink = new Drink();
    11     Iterator bunIterator = bun.creatorIterator();
    12     Iterator drinkIterator = drink.creatorIterator();
    13     printItemsWithIterator(bunIterator);
    14     printItemsWithIterator(drinkIterator);
    15   }
    16 
    17   public void printItemsWithIterator(Iterator iterator){
    18     while(iterator.hasNext()){
    19       Item item = iterator.next();
    20       System.out.println(item.getName() + ", " + item.getPrice());
    21     }
    22   }
    23 }

          输出如下:

    鲜肉包子, 1.5
    香菇青菜包子, 1.0
    鱼香肉丝包子, 1.5
    豆浆, 2.0
    八宝粥, 2.0
    牛奶, 2.5
    银耳汤, 3.0
    豆腐脑, 2.0

          仍然能够完成打印任务,而且使用迭代器模式使得代码简洁了许多,也更容易维护。

          以上简介了迭代器模式的用法,实际上有了Java内部的迭代器实现后,我们不再需要编写自己的迭代器,这里是为了展示迭代器的原理才自己实现。

  • 相关阅读:
    关于mui的主页面、子页面、页面跳转
    设计图片反复闪,点击后停止(设计定时器)
    购物车+支付宝
    登陆判读,并跳转到指定页面(window.location.href='http://localhost/index.html')
    template.js遍历对象的写法
    jqurey的跨域使用getjson(http://www.jb51.net/Special/894.htm)
    安装sass并ruby更改淘宝镜像
    Y君面试记
    MapReduce 阅读笔记
    安全感
  • 原文地址:https://www.cnblogs.com/NaLanZiYi-LinEr/p/11629069.html
Copyright © 2011-2022 走看看