迭代器模式


      迭代器模式提供了一種方法順序訪問一個聚合對象中的各個元素,而又不暴露其內部實現。

      有過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內部的迭代器實現后,我們不再需要編寫自己的迭代器,這里是為了展示迭代器的原理才自己實現。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM