前言:
迭代器模式將一個集合給封裝起來,主要是為用戶提供了一種遍歷其內部元素的方式。迭代器模式有兩個優點:①提供給用戶一個遍歷的方式,而沒有暴露其內部實現細節;②把元素之間游走的責任交給迭代器,而不是聚合對象,實現了用戶與聚合對象之間的解耦。
迭代器模式主要是通過Iterator接口來管理一個聚合對象的,而用戶使用的時候只需要拿到一個Iterator類型的對象即可完成對該聚合對象的遍歷。這里的聚合對象一般是指ArrayList,LinkedList和底層實現為數組等擁有一組相同或相似特性的對象。通過迭代器模式對聚合對象的遍歷主要是通過Iterator接口的next(),hasNext()方法進行的,這里next()方法將返回當前遍歷點的元素值,而hasNext()方法則表征當前遍歷點之后還有沒有元素。Iterator接口中還有一個remove()方法,該方法將移除當前遍歷點的元素。在一般情況下不需要使用該方法,一些特殊的情況可以調用該方法,如果當前聚合對象的遍歷不支持該操作,那么可以在該方法中跑出UnSupportedOperationException。
這里我們以如下例子來對迭代器模式進行說明。現有兩個餐廳的兩套菜單,一套菜單是使用數組實現的,而另外一套菜單是使用ArrayList實現的。現在由于兩個餐廳的合并而需要將兩套菜單進行整合,由于雙方的廚師都已經習慣了各自的菜單組裝方式,因而都希望各自繼續維護各自的菜單樣式。但是,對于服務員來說,其為顧客提供菜單的時候則必須根據兩套菜單進行兩種不同方式的處理,這必然會增加服務員的工作難度,而且,如果后期有新的餐廳合并進來,比如其使用的菜單種類為HashMap,那么服務員將又會維護這一套菜單,這也不利于擴展。根據服務員的需求,其需要的是一個菜單列表,如果其面向的是各個不同的菜單類,那么勢必會增加其工作難度,并且各個不同的菜單類中所提供的方法也不一定是服務員所需要的,因而,根據服務員的需求,這里需要制定一個菜單的規范,以實現服務員能夠按照同一種方式對其進行遍歷。這里就可以使用到迭代器模式,服務員只需要面向迭代器接口進行遍歷,而各個廚師所擁有的菜單只需要實現該迭代器即可,其依然可以按照各自的方式維護其菜單項。這樣就實現了不同的菜單與服務員的解耦。以下是使用迭代器模式解決該問題的具體代碼。
菜單接口(主要包含創建迭代器的方法):
1
2
3
|
public interface Menu<T> { Iterator<T> createIterator(); } |
菜單項:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public class MenuItem { private String name; private String description; private boolean vegetarian; private double price; public MenuItem(String name, String description, boolean vegetarian, double price) { this .name = name; this .description = description; this .vegetarian = vegetarian; this .price = price; } public String getName() { return name; } public String getDescription() { return description; } public boolean isVegetarian() { return vegetarian; } public double getPrice() { return price; } } |
菜單類(菜單項的組裝方式):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
public class DinerMenu implements Menu<MenuItem> { private static final int MAX_ITEMS = 6 ; private int numberOfItems = 0 ; private MenuItem[] menuItems; public DinerMenu() { menuItems = new MenuItem[MAX_ITEMS]; addItem( "Vegetarian BLT" , "(Fakin') Bacon with lettuce & tomato on whole wheat" , true , 2.99 ); addItem( "BLT" , "Bacon with lettuce & tomato on whole wheat" , false , 2.99 ); addItem( "Soup of the day" , "Soup of the day, with a side of potato salad" , false , 3.29 ); addItem( "Hotdog" , "A hot dog, with saurkraut, relish, onions, topped with cheese" , false , 3.05 ); } public void addItem(String name, String description, boolean vegetarian, double price) { MenuItem menuItem = new MenuItem(name, description, vegetarian, price); if (numberOfItems >= MAX_ITEMS) { System.out.println( "Sorry, menu is full, Can't add item to menu" ); } else { menuItems[numberOfItems] = menuItem; numberOfItems++; } } @Deprecated public MenuItem[] getMenuItems() { return menuItems; } public Iterator<MenuItem> createIterator() { return new DinerMenuIterator(menuItems); } } public class PancakeHouseMenu implements Menu<MenuItem> { private ArrayList<MenuItem> menuItems; public PancakeHouseMenu() { menuItems = new ArrayList<>(); addItem( "K&B's Pancake Breakfast" , "Pancakes with scrambled eggs, and toast" , true , 2.99 ); addItem( "Regular Pancake Breakfast" , "Pancakes with fried eggs, sausage" , false , 2.99 ); addItem( "Blueberry Pancakes" , "Pancakes made with fresh blueberries" , true , 3.49 ); addItem( "Waffles" , "Waffles, with your choice of blueberries or strawberries" , true , 3.49 ); } public void addItem(String name, String description, boolean vegetarian, double price) { MenuItem menuItem = new MenuItem(name, description, vegetarian, price); menuItems.add(menuItem); } @Deprecated public ArrayList<MenuItem> getMenuItems() { return menuItems; } public Iterator<MenuItem> createIterator() { return menuItems.iterator(); } } |
迭代器接口:
1
2
3
4
|
public interface Iterator<T> { boolean hasNext(); T next(); } |
迭代器類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
public class DinerMenuIterator implements Iterator<MenuItem> { private MenuItem[] items; private int position = 0 ; public DinerMenuIterator(MenuItem[] items) { this .items = items; } @Override public boolean hasNext() { return position < items.length && items[position] != null ; } @Override public MenuItem next() { return items[position++]; } @Override public void remove() { if (position <= 0 ) { throw new IllegalStateException( "You can't remove an item until you've done at least one next()" ); } if (items[position - 1 ] != null ) { for ( int i = position - 1 ; i < items.length - 1 ; i++) { items[i] = items[i + 1 ]; } items[items.length - 1 ] = null ; } } } public class PancakeHouseIterator implements Iterator<MenuItem> { private ArrayList<MenuItem> items; private int position = 0 ; public PancakeHouseIterator(ArrayList<MenuItem> items) { this .items = items; } @Override public boolean hasNext() { return position < items.size(); } @Override public MenuItem next() { return items.get(position++); } } |
服務員類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public class Waitress { private Menu<MenuItem> pancakeHouseMenu; private Menu<MenuItem> dinerMenu; public Waitress(Menu<MenuItem> pancakeHouseMenu, Menu<MenuItem> dinerMenu) { this .pancakeHouseMenu = pancakeHouseMenu; this .dinerMenu = dinerMenu; } public void printMenu() { Iterator<MenuItem> pancakeIterator = pancakeHouseMenu.createIterator(); Iterator<MenuItem> dinerIterator = dinerMenu.createIterator(); System.out.println( "MENU\n----\nBREAKFAST" ); printMenu(pancakeIterator); System.out.println( "\nLUNCH" ); printMenu(dinerIterator); } private void printMenu(Iterator<MenuItem> iterator) { while (iterator.hasNext()) { MenuItem menuItem = iterator.next(); System.out.print(menuItem.getName() + ", " ); System.out.print(menuItem.getPrice() + " -- " ); System.out.println(menuItem.getDescription()); } } } |
從上面的代碼可以看出,服務員并沒有針對具體的菜單進行編程,而是依賴于一個創建菜單迭代器的Menu接口和一個迭代器接口Iterator來進行編程的,服務員并不需要知道所傳過來的是何種組裝方式的菜單,而只需要使用實現這兩個接口的菜單對象進行遍歷即可,這就達到了將變化的依賴于多態而實現接口,將不變的依賴于接口的目的,從而實現服務員與菜單組裝方式的分離。
迭代器模式在Java類庫的集合中隨處可見,這里使用的Menu就相當于Java類庫中的Iterable接口,其作用是創建一個迭代器對象,而Iterator接口和Java類庫的Iterator接口基本一致。這里需要說明的是,實際上讓一個類實現迭代器模式在為一個類增加功能的同時也增加了該類的維護負擔,因為類的基本方法是高內聚的,所謂的內聚即是實現了一套相關的完整的功能,而迭代器接口實際上也是一套完整的相關的功能,因而讓一個類實現迭代器模式隱含地為這個類增加了兩套不那么“內聚”的兩套功能,這就會導致該類在進行功能維護的時候需要兼顧雙方。在Java類庫中ArrayList和LinkedList中就有體現,其不僅提供了List所有的基本的remove方法,也提供了迭代器所需要實現的remove方法,為了實現兩者的統一,其不得不作一些約定,比如遍歷集合的時候不能調用該類基本的remove或者add等會更改該類結構的方法。
如有疑問請留言或者到本站社區交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
原文鏈接:https://my.oschina.net/zhangxufeng/blog/794761