在閻宏博士的《JAVA與模式》一書中開頭是這樣描述迭代子(Iterator)模式的:迭代子模式又叫游標(biāo)(Cursor)模式擂红,是對象的行為模式仪际。迭代子模式可以順序地訪問一個聚集中的元素而不必暴露聚集的內(nèi)部表象(internal representation)。
迭代器模式的定義是通過提供一種方法順序訪問一個聚合對象中的各個元素,而又不必暴露該聚合對象中的內(nèi)部表示弟头。迭代器模式是針對聚合對象(數(shù)組吩抓、集合、鏈表)的“訪問”而來赴恨。通過定義不同的遍歷策略來遍歷聚合對象疹娶。
應(yīng)用場景
- 如果希望提供訪問一個聚合對象的內(nèi)容,但又不想暴露它的內(nèi)部表示的時候可使用迭代器模式
- 如果希望有多種遍歷方式可以訪問聚合對象伦连。
- 如果希望為遍歷不同的聚合對象提供一個統(tǒng)一的接口雨饺,可以使用迭代器模式(多態(tài)迭代)。
迭代器模式中涉及的角色:
- 抽象迭代器角色(Iterator):此抽象角色定義出遍歷元素的接口方法惑淳;
- 具體迭代器角色(ConcreteIterator):實現(xiàn)具體的迭代方法额港,繼承或?qū)崿F(xiàn)Iterator。
- 聚集角色(Aggregate):定義聚集對象的抽象歧焦,并定義出具體的遍歷接口返回Iterator類型
- 具體聚集角色(ConcreteAggregate):實現(xiàn)了創(chuàng)建迭代子(Iterator)對象的接口移斩,返回一個合適的具體迭代子實例。
案例演示
首先創(chuàng)建迭代器Iterator接口
/**
* 定義迭代器角色
* @author Iflytek_dsw
*
*/
interface Iterator {
/**
* 獲取第一個元素
* @return
*/
String first();
/**
* 獲取最后一個元素
* @return
*/
String last();
/**
* 判斷是否有下一個元素
* @return
*/
boolean hasNext();
/**
* 下一個元素
* @return
*/
String next();
}
在Iterator接口中定義了聚集對象需要的遍歷操作绢馍。
定義抽象聚集對象
/**
* 聚集角色向瓷,定義聚集角色具備的接口
* @author Iflytek_dsw
*
*/
abstract class Aggregate {
abstract Iterator iterator();
}
定義具體聚集對象
class ConcreteAggregate extends Aggregate{
private List<String> names;
public ConcreteAggregate(List<String> names) {
super();
this.names = names;
}
@Override
Iterator iterator() {
return new AggregateIterator(this);
}
public String first(){
return names == null ? null : names.get(0);
}
public String last(){
return names == null ? null : names.get(names.size() -1);
}
public String next(int index){
return names == null ? null : names.get(index);
}
/**
* 聚集中的元素個數(shù)
* @return
*/
public int size(){
return names.size();
}
}
在上面的實例中,我們可以看到在具體聚集中舰涌,我們定義了與Iterator對象的方法猖任,用來封裝具體的操作。以便在Iterator的具體實例中進(jìn)行調(diào)用瓷耙,同時我們也封裝了一個生成Iterator的方法朱躺。
具體迭代器
class AggregateIterator implements Iterator{
private ConcreteAggregate concreteAggregate;
private int index;
public AggregateIterator(ConcreteAggregate concreteAggregate) {
super();
this.concreteAggregate = concreteAggregate;
this.index = 0;
}
@Override
public String first() {
return concreteAggregate.first();
}
@Override
public String last() {
return concreteAggregate.last();
}
@Override
public String next() {
return concreteAggregate.next(index -1);
}
@Override
public boolean hasNext() {
if(index < concreteAggregate.size()){
index++;
return true;
}
return false;
}
}
在具體迭代器中定義包含一個聚集對象的實例,即對這個聚集對象進(jìn)行相應(yīng)操作的訪問搁痛。
客戶端
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
List<String> names = new ArrayList<>(Arrays.asList(new String[]{"James","Lucy","Jack"}));
Aggregate aggregate = new ConcreteAggregate(names);
Iterator iterator = aggregate.iterator();
System.out.println("第一個元素是:" + iterator.first());
System.out.println("最后一個元素是:" + iterator.last());
while(iterator.hasNext()){
System.out.println("遍歷元素:" + iterator.next());
}
}
}
在上面的客戶端中长搀,首先創(chuàng)建一個聚集類實例,然后調(diào)用iterator方法得到一個迭代器角色落追,得到迭代器角色后盈滴,我們就可以進(jìn)行相關(guān)的遍歷操作。
執(zhí)行結(jié)果
第一個元素是:James
最后一個元素是:Jack
遍歷元素:James
遍歷元素:Lucy
遍歷元素:Jack
通過上面的實例演示轿钠,我們可以得到以下幾點列:
- 迭代器的本質(zhì):控制訪問聚合對象中的元素巢钓。迭代器能實現(xiàn)“無須暴露聚合對象的內(nèi)部實現(xiàn),就能夠訪問到聚合對象的各個元素的功能”疗垛,做到“透明”的訪問聚合對象中的元素症汹。注意迭代器能夠即“透明”訪問,又可以“控制訪問”聚合對象贷腕。
- 迭代器的關(guān)鍵思想:把對聚合對象的遍歷訪問從聚合對象中分離出來背镇,放入單獨(dú)的迭代器中咬展,這樣聚合對象會變得簡單,而且迭代器和聚合對象可以獨(dú)立地變化和發(fā)展瞒斩,提高系統(tǒng)的靈活性破婆。
- 迭代器的動機(jī):在軟件構(gòu)建過程中,集合對象內(nèi)部結(jié)構(gòu)常常變化各異胸囱。但對于這些集合對象祷舀,我們希望在不暴露其內(nèi)部結(jié)構(gòu)的同時,可以讓外部客戶代碼透明地訪問其中包含的元素烹笔;同時這種“透明遍歷”也為“同一種算法在多種集合對象上進(jìn)行操作”提供了可能裳扯。將遍歷機(jī)制抽象為“迭代器對象”為“應(yīng)對變化中的集合對象”提供了一種優(yōu)雅的方法
在上面的例子中,既然聚集內(nèi)部已經(jīng)提供了相應(yīng)的操作方法谤职,那么我們還要使用迭代器模式呢饰豺?確實,客戶端可以根據(jù)聚集提供的接口來進(jìn)行遍歷操作允蜈,但是冤吨,迭代器模式將迭代進(jìn)行了抽象好,具有更好的擴(kuò)展性饶套,因為集合對象內(nèi)部結(jié)構(gòu)多變锅很,使用迭代器模式可將客戶端和聚集的責(zé)任分割開,使得兩者可獨(dú)立演變凤跑,迭代器模式作為中間層,可以吸收變化因素叛复,避免客戶端的修改仔引。
內(nèi)部迭代VS外部迭代
外部迭代器
指的是由客戶端來控制迭代下一個元素的步驟,客戶端會顯式調(diào)用迭代器的next()等迭代方法褐奥,在遍歷過程中向前進(jìn)行咖耘。
內(nèi)部迭代器
指的是由迭代器自己來控制迭代下一個元素的步驟。因此撬码,如果想要在迭代的過程中完成工作的話儿倒,客戶端就需要把操作傳遞給迭代器,迭代器在迭代的時候會在每個元素上執(zhí)行這個操作呜笑,類似于JAVA的回調(diào)機(jī)制夫否。
總體來說外部迭代器比內(nèi)部迭代器要靈活一些,因此我們常見的實現(xiàn)多屬于主動迭代子叫胁。
如果一個聚集的接口沒有提供修改聚集元素的方法凰慈,這樣的接口就是所謂的窄接口。
聚集對象為迭代器提供了一個寬接口驼鹅,為其它對象提供窄接口來訪問微谓。即聚集對象對迭代器是適當(dāng)公開的森篷,以便迭代器對聚集對象有足夠的了解,從而可以進(jìn)行迭代操作豺型。但是聚集對象應(yīng)該避免向其它對象提供這些方法仲智,其它對象應(yīng)該通過迭代器完成這些操作,不能直接操作聚集對象姻氨。
在Java中钓辆,實現(xiàn)這種雙重限制接口的辦法就是通過內(nèi)部類來實現(xiàn),即將迭代器對象設(shè)置為聚集類的內(nèi)部類哼绑,這樣迭代器對象可以對聚集類有很好的訪問岩馍,同時又限制了對外的操作。這種同時保證聚集對象的封裝和迭代子功能的實現(xiàn)的方案叫做黑箱實現(xiàn)方案抖韩。而這種形式定義的迭代器蛀恩,又被稱為內(nèi)部迭代器。
如下面的例子所示:
public class NameRepository implements Container {
public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};
@Override
public Iterator getIterator() {
return new NameIterator();
}
private class NameIterator implements Iterator {
int index;
@Override
public boolean hasNext() {
if(index < names.length){
return true;
}
return false;
}
@Override
public Object next() {
if(this.hasNext()){
return names[index++];
}
return null;
}
}
}
補(bǔ)充知識——靜態(tài)迭代器和動態(tài)迭代器
靜態(tài)迭代器
靜態(tài)迭代子由聚集對象創(chuàng)建茂浮,并持有聚集對象的一份快照(snapshot)双谆,在產(chǎn)生后這個快照的內(nèi)容就不再變化∠浚客戶端可以繼續(xù)修改原聚集的內(nèi)容顽馋,但是迭代子對象不會反映出聚集的新變化。
靜態(tài)迭代子的好處是它的安全性和簡易性幌羞,換言之寸谜,靜態(tài)迭代子易于實現(xiàn),不容易出現(xiàn)錯誤熊痴。但是由于靜態(tài)迭代子將原聚集復(fù)制了一份,因此它的短處是對時間和內(nèi)存資源的消耗聂宾。
動態(tài)迭代器
動態(tài)迭代子則與靜態(tài)迭代子完全相反果善,在迭代子被產(chǎn)生之后,迭代子保持著對聚集元素的引用系谐,因此巾陕,任何對原聚集內(nèi)容的修改都會在迭代子對象上反映出來。
完整的動態(tài)迭代子不容易實現(xiàn)纪他,但是簡化的動態(tài)迭代子并不難實現(xiàn)鄙煤。大多數(shù)JAVA設(shè)計師遇到的迭代子都是這種簡化的動態(tài)迭代子。為了說明什么是簡化的動態(tài)迭代子茶袒,首先需要介紹一個新的概念:Fail Fast馆类。
Fail Fast
如果一個算法開始之后,它的運(yùn)算環(huán)境發(fā)生變化弹谁,使得算法無法進(jìn)行必需的調(diào)整時乾巧,這個算法就應(yīng)當(dāng)立即發(fā)出故障信號句喜。這就是Fail Fast的含義。
如果聚集對象的元素在一個動態(tài)迭代子的迭代過程中發(fā)生變化時沟于,迭代過程會受到影響而變得不能自恰咳胃。這時候,迭代子就應(yīng)當(dāng)立即拋出一個異常旷太。這種迭代子就是實現(xiàn)了Fail Fast功能的迭代子展懈。
Fail Fast在JAVA聚集中的使用
JAVA語言以接口java.util.Iterator的方式支持迭代子模式,Collection接口要求提供iterator()方法供璧,此方法在調(diào)用時返還一個Iterator類型的對象存崖。而作為Collection接口的子類型,AbstractList類的內(nèi)部成員類Itr便是實現(xiàn)Iterator接口的類睡毒。
優(yōu)缺點
優(yōu)點
- 更好的封裝性来惧,聚集對象不需要暴露內(nèi)部實現(xiàn)即可實現(xiàn)訪問;
- 每一個聚集對象都可以有一個或多個迭代子對象演顾,每一個迭代子的迭代狀態(tài)可以是彼此獨(dú)立的供搀。因此,一個聚集對象可以同時有幾個迭代在進(jìn)行之中钠至。
- 聚集內(nèi)容和迭代器的分離葛虐,提高了擴(kuò)展性,即可以使用不同的迭代器來遍歷聚集內(nèi)容棉钧。
缺點
- 迭代器種類過多屿脐,會導(dǎo)致類的個數(shù)增加,提高了系統(tǒng)的復(fù)雜性宪卿;
- 在動態(tài)迭代器中易發(fā)生異常摄悯。
參考資料