設計模式(行為型)之迭代器模式(Iterator Pattern)
PS一句:最終還是選擇CSDN來整理髮表這幾年的知識點,該文章平行遷移到CSDN。因為CSDN也支援MarkDown語法了,牛逼啊!
概述
在軟體構建過程中,集合物件內部結構常常變化各異。但對於這些集合物件,我們希望在不暴露其內部結構的同時,可以讓外部客戶程式碼透明地訪問其中包含的元素;同時這種“透明遍歷”也為“ 同一種演算法在多種集合物件上進行操作”提供了可能。使用面向物件技術將這種遍歷機制抽象為“迭代器物件”為“應對變化中的集合物件”提供了一種優雅的方法。
核心
概念: 提供一種方法來訪問聚合物件,而不用暴露這個物件的內部表示,其別名為遊標(Cursor)。迭代器模式是一種物件行為型模式。
迭代器模式結構重要核心模組:
迭代器角色(Iterator)
迭代器角色負責定義訪問和遍歷元素的介面。
具體迭代器角色(Concrete Iterator)
具體迭代器角色要實現迭代器介面,並要記錄遍歷中的當前位置。
容器角色(Container)
容器角色負責提供建立具體迭代器角色的介面。
具體容器角色(Concrete Container)
具體容器角色實現建立具體迭代器角色的介面——這個具體迭代器角色於該容器的結構相關。
迭代器模式中應用了工廠方法模式,抽象迭代器對應於抽象產品角色,具體迭代器對應於具體產品角色,抽象聚合類對應於抽象工廠角色,具體聚合類對應於具體工廠角色。
使用場景
訪問一個聚合物件的內容而無需暴露它的內部表示。
支援對聚合物件的多種遍歷。
為遍歷不同的聚合結構提供一個統一的介面(即, 支援多型迭代)。
迭代器模式是與集合共生共死的,一般來說,我們只要實現一個集合,就需要同時提供這個集合的迭代器,就像java中的Collection,List、Set、Map等,這些集合都有自己的迭代器。假如我們要實現一個這樣的新的容器,當然也需要引入迭代器模式,給我們的容器實現一個迭代器。但是,由於容器與迭代器的關係太密切了,所以大多數語言在實現容器的時候都給提供了迭代器,並且這些語言提供的容器和迭代器在絕大多數情況下就可以滿足我們的需要,所以現在需要我們自己去實踐迭代器模式的場景還是比較少見的,我們只需要使用語言中已有的容器和迭代器就可以了。
程式猿例項
如下示例是一個簡單的迭代器例項程式,不做過多解釋:
package yanbober.github.io;
import java.util.ArrayList;
import java.util.List;
//容器角色(Container)
interface Container {
void add(Object obj);
void remove(Object obj);
Iterator createIterator();
}
//具體容器角色(Concrete Container)
class ConcreteContainer implements Container {
private List<Object> list;
public ConcreteContainer(List<Object> list) {
this.list = list;
}
@Override
public void add(Object obj) {
list.add(obj);
}
@Override
public void remove(Object obj) {
list.remove(obj);
}
@Override
public Iterator createIterator() {
return new ConcreteIterator(list);
}
}
//迭代器角色(Iterator)
interface Iterator {
Object first();
Object next();
boolean hasNext();
Object currentItem();
}
//具體迭代器角色(Concrete Iterator)
class ConcreteIterator implements Iterator {
private List<Object> list;
private int cursor;
public ConcreteIterator(List<Object> list) {
this.list = list;
}
@Override
public Object first() {
cursor = 0;
return list.get(cursor);
}
@Override
public Object next() {
Object ret = null;
if (hasNext()) {
ret = list.get(cursor);
}
cursor++;
return ret;
}
@Override
public boolean hasNext() {
return !(cursor == list.size());
}
@Override
public Object currentItem() {
return list.get(cursor);
}
}
//客戶端
public class Main {
public static void main(String[] args) {
List<Object> list = new ArrayList<Object>();
list.add("Android");
list.add("PHP");
list.add("C Language");
Container container = new ConcreteContainer(list);
container.add("HardWare");
Iterator iterator = container.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
升個級!使用內部類實現迭代器模式:
上面的例子可以看到具體迭代器類和具體聚合類之間存在雙重關係,其中一個關係為關聯關係,在具體迭代器中需要維持一個對具體聚合物件的引用,該關聯關係的目的是訪問儲存在聚合物件中的資料,以便迭代器能夠對這些資料進行遍歷操作。除了使用關聯關係外,為了能夠讓迭代器可以訪問到聚合物件中的資料,我們還可以將迭代器類設計為聚合類的內部類。如下程式碼示例就是對上面程式碼的改良版本。 其實無論使用哪種方式,客戶端程式碼都是一樣的。客戶端用不著關心具體迭代器物件的建立細節,只需通過呼叫工廠方法createIterator()即可得到一個可用的迭代器物件,這也是使用工廠方法模式的好處,通過工廠來封裝物件的建立過程,簡化了客戶端的呼叫。
package yanbober.github.io;
import java.util.ArrayList;
import java.util.List;
//容器角色(Container)
interface Container {
void add(Object obj);
void remove(Object obj);
Iterator createIterator();
}
//具體容器角色(Concrete Container)
class ConcreteContainer implements Container {
private List<Object> list;
public ConcreteContainer(List<Object> list) {
this.list = list;
}
@Override
public void add(Object obj) {
list.add(obj);
}
@Override
public void remove(Object obj) {
list.remove(obj);
}
@Override
public Iterator createIterator() {
return new ConcreteIterator();
}
//具體迭代器角色(Concrete Iterator)
class ConcreteIterator implements Iterator {
private int cursor;
public ConcreteIterator() {
}
@Override
public Object first() {
cursor = 0;
return list.get(cursor);
}
@Override
public Object next() {
Object ret = null;
if (hasNext()) {
ret = list.get(cursor);
}
cursor++;
return ret;
}
@Override
public boolean hasNext() {
return !(cursor == list.size());
}
@Override
public Object currentItem() {
return list.get(cursor);
}
}
}
//迭代器角色(Iterator)
interface Iterator {
Object first();
Object next();
boolean hasNext();
Object currentItem();
}
//客戶端
public class Main {
public static void main(String[] args) {
List<Object> list = new ArrayList<Object>();
list.add("Android");
list.add("PHP");
list.add("C Language");
Container container = new ConcreteContainer(list);
container.add("HardWare");
Iterator iterator = container.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
總結一把
迭代器模式優點:
它支援以不同的方式遍歷一個聚合物件,在同一個聚合物件上可以定義多種遍歷方式。
迭代器簡化了聚合類。由於引入了迭代器,在原有的聚合物件中不需要再自行提供資料遍歷等方法,這樣可以簡化聚合類的設計。
在迭代器模式中,由於引入了抽象層,增加新的聚合類和迭代器類都很方便,無須修改原有程式碼,滿足“開閉原則”的要求。
迭代器模式缺點:
由於迭代器模式將儲存資料和遍歷資料的職責分離,增加新的聚合類需要對應增加新的迭代器類,類的個數成對增加,這在一定程度上增加了系統的複雜性。
抽象迭代器的設計難度較大,需要充分考慮到系統將來的擴充套件,例如JDK內建迭代器Iterator就無法實現逆向遍歷,如果需要實現逆向遍歷,只能通過其子類ListIterator等來實現,而ListIterator迭代器無法用於操作Set型別的聚合物件。在自定義迭代器時,建立一個考慮全面的抽象迭代器並不是件很容易的事情。