說明:本文介紹設計模式中,行為型設計模式之一的迭代器模式。
定義
迭代器模式(Iterator Pattern),也叫作游標模式(Cursor Pattern),它提供一種按順序訪問集合/容器對象元素的方法,而又無須暴露集合內部表示。迭代器模式可以為不同的容器提供一致的遍歷行為,而不用關心容器內元素的組成結構。(引自《設計模式就該這樣學》P329)
行車記錄儀場景
假設有一個行車記錄儀對象,可存儲行車記錄時的視頻,可存儲10個視頻,超出會覆蓋最早的數據,如下:
/*** 行車記錄儀*/
public class DrivingRecorder {/*** 當前記錄的位置*/private int index = -1;/*** 假設只能存儲10個視頻*/private String[] records = new String[10];/*** 存入*/public void append(String record) {// 如果當前位置已經到達末尾,就從頭開始存儲if (index == 9) {index = 0;} else {index++;}records[index] = record;}/*** 順序遍歷*/public void display() {for (int i = 0; i < 10; i++) {System.out.println(i + ":" + records[i]);}}/*** 按照存入順序逆序遍歷* 從新=>舊讀取*/public void displayByOrder() {// loopCount:是集合能存儲的數據個數,故不能大于10;// i是記錄的位置,存的時候+1,讀的時候-1,并且還要做==0判斷,如果==0,就從集合末尾開始讀;for (int i = index, loopCount = 0; loopCount < 10; i = i == 0 ? 9 : i - 1, loopCount++) {System.out.println(records[i]);}}
}
運行如下,可見存入12個視頻,把前面存儲的兩個視頻數據覆蓋了。
分析
(1)無法讀取到行車記錄儀中的數據(即變量records),當然我們可以開放對應的get方法,但這樣設計遍歷和讀取數據方法不免有重復(現成的index變量還沒用上,不可惜嘛?),我們能否擴展遍歷方法,返回當前位置上的數據?;
(2)代碼不夠優雅,對于一個封閉的對象或者說容器,遍歷對象內的數據,我們是否可以考慮抽出成一個接口,定義遍歷的規范,使其他對象實現其接口。
迭代器設計
針對上面行車記錄儀場景,改造成迭代器設計模式,如下:
import java.util.Iterator;/*** 行車記錄儀(迭代器設計)*/
public class DrivingRecorderIterable implements Iterable<String> {/*** 當前記錄的位置*/private int index = -1;/*** 假設只能存儲10個視頻*/private String[] records = new String[10];/*** 存入*/public void append(String record) {// 如果當前位置已經到達末尾,就從頭開始存儲if (index == 9) {index = 0;} else {index++;}records[index] = record;}@Overridepublic Iterator<String> iterator() {return new Itr();}/*** 行車記錄儀迭代器*/private class Itr implements Iterator<String> {/*** cursor:游標,這里通過賦值拷貝一份,不要直接使用index,不然一邊讀一邊寫的時候會出錯* loopCount:是集合能存儲的數據個數,故不能大于10*/int cursor = index;int loopCount = 0;@Overridepublic boolean hasNext() {return loopCount < 10;}@Overridepublic String next() {int i = cursor;if (i == 9) {i = 0;} else {i++;}cursor = i;loopCount++;return records[i];}}
}
這里使用的接口是JDK自帶的Iterator
,實現該接口的類都能使用上述方式遍歷數據。
Java中的單列集合,Collection,實現了該接口,也就是說實現了Collection接口的容器,都支持這種迭代器的遍歷方式。
像ArrayList
List<String> list = new ArrayList<>();list.add("王麻子");list.add("小李子");list.add("李愛花");Iterator<String> iterator = list.iterator();while (iterator.hasNext()){System.out.println(iterator.next());}
使用場景
在《設計模式就該這樣學》(P330)這本書中,提到迭代器模式適用于以下場景:
(1)訪問一個集合對象的內容而無須暴露它的內部表示。
(2)為遍歷不同的集合結構提供一個統一的訪問接口;
結合上述行車記錄儀場景,如果你需要訪問一個對象中的數據,又不想開放對應數據的get方法,就可以考慮迭代器模式。
總結
本文介紹了行為型設計模式中的迭代器模式,參考《設計模式就該這樣學》、《秒懂設計模式》兩書,行車記錄儀場景是《秒懂設計模式》中的舉例,非常形象,容易理解。