迭代器模式

Posted nalanziyi-liner

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了迭代器模式相关的知识,希望对你有一定的参考价值。

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

      有过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内部的迭代器实现后,我们不再需要编写自己的迭代器,这里是为了展示迭代器的原理才自己实现。

以上是关于迭代器模式的主要内容,如果未能解决你的问题,请参考以下文章

设计模式之迭代器模式与命令模式详解和应用

迭代器模式及php实现

设计模式-迭代器模式

Java设计模式之迭代器模式

设计模式11-迭代器模式与命令模式详解

设计模式之三:迭代器模式(IteratorPattern)