迭代器模式是一種使用頻率非常高的設(shè)計模式,,迭代器用于對一個聚合對象進行遍歷。通過引入迭代器可以將數(shù)據(jù)的遍歷功能從聚合對象中分離出來,,聚合對象只負責(zé)存儲數(shù)據(jù),,聚合對象只負責(zé)存儲數(shù)據(jù),而遍歷數(shù)據(jù)由迭代器來完成,。
模式動機
一個聚合對象,,如一個列表(List)或者一個集合(Set),應(yīng)該提供一種方法來讓別人可以訪問它的元素,而又不需要暴露它的內(nèi)部結(jié)構(gòu),。此外,,針對不同的需要,可能還要以不同方式遍歷整個聚合對象,,但是我們不希在聚合對象的抽象層接口中充斥著各種不同遍歷的操作,。怎樣遍歷一個聚合對象,又不需要了解聚合對象的內(nèi)部結(jié)構(gòu),,還能提供多種不同的遍歷方式,,這就是迭代器模式所要解決的問題,。
迭代器模式中,提供一個外部的迭代器來對聚合對象進行訪問和遍歷,,迭代器定義一個訪問該聚合元素的接口,,并且可以跟蹤當前遍歷對象,了解哪些元素已經(jīng)遍歷過而哪些沒有,。
模式定義
提供一種方法來訪問聚合對象,,而不用暴露這個對象的內(nèi)部表示,其別名為游標(Cursor),。迭代器模式是一種對象行為模式,。
模式結(jié)構(gòu)
-
Iterator(抽象迭代器)
抽象迭代器定義了訪問和遍歷元素的接口,一般聲明以下方法:
- 用于獲取第一個元素的 first()
- 用于訪問下一個元素的 next()
- 用于判斷是否還有下一個元素的 hasNext()
- 用于獲取當前元素的 currentItem()
-
ConcreteIterator(具體迭代器)
具體迭代器實現(xiàn)了抽象迭代器接口,,完成對聚合對象的遍歷,,同時在對聚合進行遍歷時跟蹤其當前位置
-
Aggregate(抽象聚合類)
抽象聚合類用于存儲對象,并定義創(chuàng)建相應(yīng)迭代器對象的接口,,聲明一個 createIterator() 方法用于創(chuàng)建一個迭代器對象
-
ConcreteAggregate(具體聚合類)
具體聚合類實現(xiàn)了創(chuàng)建相應(yīng)迭代器的接口,,實現(xiàn)了在聚合類中聲明的 createIterator() 方法,該方法返回一個與具體聚合對應(yīng)的具體迭代器 ConcreteIterator 實例
模式分析
存儲數(shù)據(jù)是聚合對象的最基本職責(zé),,其中包含存儲數(shù)據(jù)的類型,、存儲空間的大小、存儲空間的分配,,以及存儲的方式和順序,。然而,聚合對象除了能存儲數(shù)據(jù)外,,還必須提供遍歷訪問其內(nèi)部數(shù)據(jù)的方式,,同時這些遍歷方式可能會根據(jù)不同的情形提供不同的實現(xiàn)。
因此,,聚合對象主要有兩個職責(zé):一是存儲內(nèi)部數(shù)據(jù),;二是遍歷內(nèi)部數(shù)據(jù)。前者是聚合對象的基本功能,,后者是可以分離的,。根據(jù)單一職責(zé)原則,對象承擔的職責(zé)越少,,對象的穩(wěn)定性就越好,,我們將遍歷聚合對象中數(shù)據(jù)的行為提取出來,封裝到一個迭代器中,,通過專門的迭代器來遍歷聚合對象的內(nèi)部數(shù)據(jù),。迭代器模式是單一職責(zé)原則的完美體現(xiàn),。
下面通過一個簡單的自定義迭代器來分析迭代器模式的結(jié)構(gòu)
首先定義一個簡單的迭代器去接口
public interface MyIterator {
void first();// 訪問第一個元素
void next();// 訪問下一個元素
boolean isLast();// 判斷是否是最后一個元素
Object currentItem();// 獲取當前元素
}
然后需要定義一個聚合接口
public interface MyCollection {
// 返回一個 MyIterator 迭代器對象
MyIterator createIterator();
}
定義好抽象層之后,,我們需要定義抽象迭代器接口和抽象聚合接口的實現(xiàn)類,,一般將具體迭代器類作為具體聚合類的內(nèi)部類,從而迭代器可以實現(xiàn)直接訪問聚合類中的數(shù)據(jù)
public class NewCollection implements MyCollection {
private Object[] obj = {"dog", "pig", "cat", "monkey", "pig"};
public MyIterator createIterator() {
return new NewIterator();
}
private class NewIterator implements MyIterator {
private int currentIndex = 0;
public void first() {
currentIndex = 0;
}
public void next() {
if(currentIndex < obj.length) {
currentIndex++;
}
}
public boolean isLast() {
return currentIndex == obj.length;
}
public void currentItem() {
return obj[currentIndex];
}
}
}
NewCollection 類實現(xiàn)了 MyCollection 接口,,實現(xiàn)了 createIterator() 方法,,同時定義了一個數(shù)組用于存儲數(shù)據(jù)元素,還定義了一個實現(xiàn)了 MyIterator 接口的內(nèi)部類,,索引變量 currentIndex 用于保存所操作的數(shù)組元素的下標值,。客戶端代碼如下:
public class Client {
public static void process(MyCollection collection) {
MyIterator i = collection.createIterator();
while(!i.isLast()) {
System.out.println(i.currentItem().toString());
i.next();
}
}
public static void main(String args[]) {
MyCollection collection = new NewCollection();
process(collection);
}
}
除了使用內(nèi)部類實現(xiàn)之外,,也可以使用常規(guī)的方式來實現(xiàn)迭代器
public class ConcreteIterator implements Iterator {
private ConcreteAggregate objects;
public ConcreteIterator(ConcreteAggregate objects) {
this.objects = objects;
}
public void first() {
...
}
public void next() {
...
}
public boolean isLast() {
...
}
public void currentItem() {
...
}
}
public class ConcreteAggregate implements Aggregate {
...
public Iterator createIterator() {
return new ConcreteIterator(this);
}
}
迭代器模式中應(yīng)用了工廠方法模式,,聚合類充當工廠類,而迭代器充當產(chǎn)品類
模式優(yōu)缺點
迭代器模式優(yōu)點:
- 支持以不同的方式遍歷一個聚合對象,。在迭代器模式中只需要用一個不同的迭代器來替換原有迭代器即可改變遍歷算法,,也可以自己定義迭代器的子類以支持新的遍歷方式。
- 迭代器簡化了聚合類,。原有聚合對象不再需要自行提供遍歷數(shù)據(jù)等操作方法,。
- 在同一個聚合上可以有多個遍歷。由于每個迭代器都保持自己的遍歷狀態(tài),,因此可以對一個聚合對象進行多個遍歷操作,。
- 增加新的聚合類和迭代器類都很方便,無須修改原代碼,,滿足開閉原則,。
迭代器模式缺點:
- 由于迭代器模式將存儲數(shù)據(jù)和遍歷數(shù)據(jù)的職責(zé)分離,增加新的聚合類需要對應(yīng)增加新的迭代器類,,類的個數(shù)成對增加,,在一定程度上增加了系統(tǒng)的復(fù)雜性
模式適用環(huán)境
在以下情況可以使用迭代器模式:
- 訪問一個聚合對象的內(nèi)容而無須暴露它的內(nèi)部表示
- 需要為聚合對象提供多種遍歷方式
- 為遍歷不同的聚合結(jié)構(gòu)提供一個統(tǒng)一的接口
Java 迭代器
Java 中的集合框架 Collections,其基本接口層次結(jié)構(gòu)如圖
Collection 是所有集合類的根接口,,它的主要方法如下:
boolean add(Object c);
boolean addAll(Collection c);
boolean remove(Object o);
boolean removeAll(Collection c);
boolean remainAll(Collection c);
Iterator iterator();
Collection 的 iterator() 方法返回一個 java.util.Iterator 類型的對象,,而其子接口 java.util.List 的 listIterator() 方法返回一個 java.util.ListIterator 類型的對象,ListIterator 是 Iterator 的子類,,它們構(gòu)成了 Java 語言對迭代器模式的支持,。
在 JDK 中,Iterator 接口具有如下三個基本方法:
- Object next():通過反復(fù)調(diào)用 next() 方法可以逐個訪問聚合中的元素
- boolean hasNext():用于判斷聚合對象在是否還存在下一個元素,,為了不拋出異常,,必須在調(diào)用 next() 之前先調(diào)用 hasNext()。如果迭代對象仍然擁有可供訪問的元素,,那么 hasNext() 返回 true
- void remove():刪除上一次調(diào)用 next() 時返回的元素
Java 迭代器可以理解為它工作時在聚合對象的各個元素之間,,每調(diào)用一次 next() 方法,迭代器便越過下個元素,,并且返回它剛越過的那個元素的地址引用,。
在第一個 next() 方法被調(diào)用時,,迭代器由“元素1”與“元素2”之間移動至“元素2”與“元素3”之間,跨越了“元素2”,,因此 next() 方法將返回對“元素2”的引用,;在第二個 next() 方法被調(diào)用時,迭代器由“元素2”與“元素3”之間移至“元素3”與“元素4”之間,,next() 方法將返回對“元素3”的引用,,此時調(diào)用 remove() 方法,則可將”元素3“刪除,。
需要注意的是,,next() 方法與 remove() 方法的調(diào)用是相互關(guān)聯(lián)的。如果調(diào)用 remove() 之前沒有先對 next() 進行調(diào)用,,那么將拋出異常,,因為沒有任何可供刪除的元素
|