迭代器模式
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内部的迭代器实现后,我们不再需要编写自己的迭代器,这里是为了展示迭代器的原理才自己实现。
以上是关于迭代器模式的主要内容,如果未能解决你的问题,请参考以下文章