一、介紹
迭代器模式(Iterator Pattern)又稱為游標(Cursor)模式,是行為型設計模式之一。迭代器模式算是一個比較古老的設計模式,其源于對容器的訪問,比如Java中的List、Map、數組等,我們知道對容器對象的訪問必然會涉及遍歷算法,我們可以將遍歷的方法封裝在容器中,或者不提供遍歷方法。如果我們將遍歷的方法封裝到容器中,那么對于容器類來說就承擔了過多的功能,容器類不僅要維護自身內部的數據元素而且還要對外提供遍歷的接口方法,因為遍歷狀態的存儲問題還不能對同一個容器同時進行多個遍歷操作,如果我們不提供遍歷方法而讓使用者自己去實現,又會讓容器的內部細節暴露無遺,正因于此,迭代模式應運而生,在客戶訪問類與容器體之間插入了一個第三者——迭代器,很好地解決了上面所述的弊端。
二、定義
提供一種方法順序訪問一個容器對象中的各個元素,而又不需要暴露該對象的內部表示。
三、使用場景
- 遍歷一個容器對象。
四、迭代器模式的UML類圖
UML類圖:
通用模式代碼:
迭代器接口:
public interface Iterator<T> {/*** 是否還有下一個元素* @return true表示有,false表示沒有**/boolean hasNext();/*** 返回當前位置的元素并將位置移至下一位**/T next();
}
具體迭代器類:
public class ConcreteIterator<T> implements Iterator<T>{private List<T> list;private int cursor = 0;public ConcreteIterator(List<T> list) {this.list = list;}@Overridepublic boolean hasNext() {return cursor != list.size();}@Overridepublic T next() {T obj = null;if (this.hasNext()) {obj = this.list.get(cursor++);}return obj;}
}
容器接口:
public interface Aggregation<T> {/*** 添加一個元素**/void add(T obj);/*** 移除一個元素**/void remove(T obj);/*** 獲取容器的迭代器**/Iterator<T> iterator();
}
具體容器類:
public class ConcreteAggregation<T> implements Aggregation<T>{private List<T> list = new ArrayList<>();@Overridepublic void add(T obj) {list.add(obj);}@Overridepublic void remove(T obj) {list.remove(obj);}@Overridepublic Iterator<T> iterator() {return new ConcreteIterator<>(list);}
}
客戶類:
public class Client {public static void main(String args[]) {Aggregation<String> a = new ConcreteAggregation<>();a.add("a");a.add("b");a.add("c");Iterator<String> iterator = a.iterator();while (iterator.hasNext()) {System.out.print(iterator.next());}}
}
角色介紹:
Iterator:迭代器接口,迭代器接口負責定義、訪問和遍歷元素的接口。
ConcreteIterator:具體迭代器類,具體迭代器類的目的主要是實現迭代器接口,并記錄遍歷的當前位置。
Aggregate:容器接口,容器接口負責提供創建具體迭代器角色的接口。
ConcreteAggregate:具體容器類,具體迭代器角色與該容器相關聯。
Client:客戶類。
五、簡單實現
小民和小輝分別在公司的兩個事業部,某天老板安排任務讓他們倆統計一下各自部門的員工數據,這很好辦嘛,建一個類用數據結構把所有員工數據存進去即可,老板要看的時候給他用for循環實現,還是比較容易的,下面就先為員工創建一個實體類:
員工實體類:
public class Employee {private String name;// 姓名private int age;// 年齡private String sex;// 性別private String position;// 職位public Employee(String name, int age, String sex, String position) {super();this.name = name;this.age = age;this.sex = sex;this.position = position;}// 簡化代碼,省略setter和getter方法@Overridepublic String toString() {return "Employee{" + "name='" + name + '\'' + ", age=" + age + ", sex="+ sex + ", position='" + position + '\'' + "}";}
}
小民部門:
public class CompanyMin {private List<Employee> list = new ArrayList<>();public CompanyMin(){list.add(new Employee("小民", 26, "男", "程序猿"));list.add(new Employee("小蕓", 22, "女", "測試"));list.add(new Employee("小方", 18, "女", "測試"));list.add(new Employee("可兒", 21, "女", "設計"));list.add(new Employee("朗情", 19, "女", "設計")); //吐槽一下,為什么就小民一個男的,小輝部門全男的。}public List<Employee> getEmployees(){return list;}
}
小輝部門:
public class CompanyHui {private Employee[] array = new Employee[3];public CompanyHui(){array[0] = new Employee("輝哥", 28, "男", "程序猿");array[1] = new Employee("小紅", 23, "男", "程序猿");array[2] = new Employee("小輝", 25, "男", "程序猿");}public Employee[] getEmployees(){return array;}
}
可見小民和小輝的內部實現是兩種方式,小民的人員信息容器的內部實質是使用的一個List類存儲人員信息,而小輝的實質上使用的是一個數組,如果老板要查看人員信息就必須遍歷兩個容器:
Boss查看:
public class Boss {public static void main(String[] args) {CompanyHui hui = new CompanyHui();Employee[] huiList = hui.getEmployees();for(int i = 0; i < huiList.length; i++){System.out.println(huiList[i]);}CompanyMin min = new CompanyMin();List minList = min.getEmployees();for(int i = 0; i < minList.size(); i++){System.out.println(minList.get(i).toString());}}
}
結果:
Employee{name='輝哥', age=28, sex=男, position='程序猿'}
Employee{name='小紅', age=23, sex=男, position='程序猿'}
Employee{name='小輝', age=25, sex=男, position='程序猿'}
Employee{name='小民', age=26, sex=男, position='程序猿'}
Employee{name='小蕓', age=22, sex=女, position='測試'}
Employee{name='小方', age=18, sex=女, position='測試'}
Employee{name='可兒', age=21, sex=女, position='設計'}
Employee{name='朗情', age=19, sex=女, position='設計'}
這樣看似也沒有問題,但是如果有多個部門,每個部門有各自的實現,那么我們就要在Boss類中增加一遍遍歷邏輯,這樣Boss類的功能會越來越多,同時暴露了內部細節。那么我們需要定義一個迭代器接口:
public interface Iterator {/*** 是否還有下一個元素 * * @return true表示有,false表示沒有*/boolean hasNext();/*** 返回當前元素,并將位置移至下一位*/Object next();
}
小民的迭代器:
public class MinIterator implements Iterator{private List<Employee> list;private int position;public MinIterator(List<Employee> list){this.list = list;}@Overridepublic boolean hasNext() {return !(position > list.size() - 1 || list.get(position) == null);}@Overridepublic Object next() {Employee e = list.get(position);position++;return e;}}
小輝的迭代器:
public class HuiIterator implements Iterator{private Employee[] array;private int position;public HuiIterator(Employee[] array){this.array = array;}@Overridepublic boolean hasNext() {return !(position > array.length - 1 || array[position] == null);}@Overridepublic Object next() {Employee e = array[position];position++;return e;}}
定義容器類的接口:
public interface Company {/*** 返回一個迭代器對象* * @return 迭代器對象*/Iterator iterator();}
修改一下之前的兩個容器類:
public class CompanyHui implements Company{private Employee[] array = new Employee[3];public CompanyHui(){array[0] = new Employee("輝哥", 28, "男", "程序猿");array[1] = new Employee("小紅", 23, "男", "程序猿");array[2] = new Employee("小輝", 25, "男", "程序猿");}public Employee[] getEmployees(){return array;}@Overridepublic Iterator iterator() {return new HuiIterator(array);}
}
public class CompanyMin implements Company{private List<Employee> list = new ArrayList<>();public CompanyMin(){list.add(new Employee("小民", 26, "男", "程序猿"));list.add(new Employee("小蕓", 22, "女", "測試"));list.add(new Employee("小方", 18, "女", "測試"));list.add(new Employee("可兒", 21, "女", "設計"));list.add(new Employee("朗情", 19, "女", "設計"));}public List<Employee> getEmployees(){return list;}@Overridepublic Iterator iterator() {return new MinIterator(list);}
}
Boss查看:
public class Boss {public static void main(String[] args) {CompanyHui hui = new CompanyHui();check(hui.iterator());CompanyMin min = new CompanyMin();check(min.iterator());}private static void check(Iterator iterator){while (iterator.hasNext()) {System.out.println(iterator.next().toString());}}
}
六、Android源碼中的迭代器模式
1、Cursor
當我們使用SQLiteDatabase的query方法查詢數據庫時,會返回一個Cursor游標對象,該游標的實質就是一個具體的迭代器,我們可以使用它來遍歷數據庫查詢所得的結果集。
七、總結
迭代器模式發展至今,幾乎所有的高級語言都有相應的內置實現,對于開發者而言,已經極少會自己去實現迭代器了,所以本章內容更多的是了解而非應用。
優點:
符合面向對象設計原則中的單一職責原則。
支持對容器對象的多種遍歷。弱化了容器類與遍歷算法之間的關系。
缺點:
類文件的增加。
會出現ConcurrentModificationException異常。
遍歷過程是一個單向且不可逆的遍歷。