設(shè)計模式——迭代器模式

在閻宏博士的《JAVA與模式》一書中開頭是這樣描述迭代子(Iterator)模式的:迭代子模式又叫游標(biāo)(Cursor)模式擂红,是對象的行為模式仪际。迭代子模式可以順序地訪問一個聚集中的元素而不必暴露聚集的內(nèi)部表象(internal representation)。

迭代器模式的定義是通過提供一種方法順序訪問一個聚合對象中的各個元素,而又不必暴露該聚合對象中的內(nèi)部表示弟头。迭代器模式是針對聚合對象(數(shù)組吩抓、集合、鏈表)的“訪問”而來赴恨。通過定義不同的遍歷策略來遍歷聚合對象疹娶。

應(yīng)用場景

  1. 如果希望提供訪問一個聚合對象的內(nèi)容,但又不想暴露它的內(nèi)部表示的時候可使用迭代器模式
  2. 如果希望有多種遍歷方式可以訪問聚合對象伦连。
  3. 如果希望為遍歷不同的聚合對象提供一個統(tǒng)一的接口雨饺,可以使用迭代器模式(多態(tài)迭代)。
IteratorPattern.png

迭代器模式中涉及的角色:

  • 抽象迭代器角色(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

通過上面的實例演示轿钠,我們可以得到以下幾點列:

  1. 迭代器的本質(zhì):控制訪問聚合對象中的元素巢钓。迭代器能實現(xiàn)“無須暴露聚合對象的內(nèi)部實現(xiàn),就能夠訪問到聚合對象的各個元素的功能”疗垛,做到“透明”的訪問聚合對象中的元素症汹。注意迭代器能夠即“透明”訪問,又可以“控制訪問”聚合對象贷腕。
  2. 迭代器的關(guān)鍵思想:把對聚合對象的遍歷訪問從聚合對象中分離出來背镇,放入單獨(dú)的迭代器中咬展,這樣聚合對象會變得簡單,而且迭代器和聚合對象可以獨(dú)立地變化和發(fā)展瞒斩,提高系統(tǒng)的靈活性破婆。
  3. 迭代器的動機(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)點

  1. 更好的封裝性来惧,聚集對象不需要暴露內(nèi)部實現(xiàn)即可實現(xiàn)訪問;
  2. 每一個聚集對象都可以有一個或多個迭代子對象演顾,每一個迭代子的迭代狀態(tài)可以是彼此獨(dú)立的供搀。因此,一個聚集對象可以同時有幾個迭代在進(jìn)行之中钠至。
  3. 聚集內(nèi)容和迭代器的分離葛虐,提高了擴(kuò)展性,即可以使用不同的迭代器來遍歷聚集內(nèi)容棉钧。

缺點

  1. 迭代器種類過多屿脐,會導(dǎo)致類的個數(shù)增加,提高了系統(tǒng)的復(fù)雜性宪卿;
  2. 在動態(tài)迭代器中易發(fā)生異常摄悯。

參考資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市愧捕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌申钩,老刑警劉巖次绘,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異撒遣,居然都是意外死亡邮偎,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門义黎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來禾进,“玉大人,你說我怎么就攤上這事廉涕⌒涸疲” “怎么了艇拍?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長宠纯。 經(jīng)常有香客問我卸夕,道長,這世上最難降的妖魔是什么婆瓜? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任快集,我火速辦了婚禮,結(jié)果婚禮上廉白,老公的妹妹穿的比我還像新娘个初。我一直安慰自己,他們只是感情好猴蹂,可當(dāng)我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布院溺。 她就那樣靜靜地躺著,像睡著了一般晕讲。 火紅的嫁衣襯著肌膚如雪覆获。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天瓢省,我揣著相機(jī)與錄音弄息,去河邊找鬼。 笑死勤婚,一個胖子當(dāng)著我的面吹牛摹量,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播馒胆,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼缨称,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了祝迂?” 一聲冷哼從身側(cè)響起睦尽,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎型雳,沒想到半個月后当凡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡纠俭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年沿量,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冤荆。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡朴则,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出钓简,到底是詐尸還是另有隱情乌妒,我是刑警寧澤汹想,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站芥被,受9級特大地震影響欧宜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拴魄,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一冗茸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧匹中,春花似錦夏漱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至服赎,卻和暖如春葵蒂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背重虑。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工践付, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缺厉。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓永高,卻偏偏與公主長得像,于是被迫代替她去往敵國和親提针。 傳聞我的和親對象是個殘疾皇子命爬,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容

  • 目錄 本文的結(jié)構(gòu)如下: 引言 什么是迭代器模式 模式的結(jié)構(gòu) 典型代碼 代碼示例 優(yōu)點和缺點 適用環(huán)境 模式應(yīng)用 一...
    w1992wishes閱讀 513評論 0 1
  • 1 場景問題# 1.1 工資表數(shù)據(jù)的整合## 考慮這樣一個實際應(yīng)用:整合工資表數(shù)據(jù)。 這個項目的背景是這樣的辐脖,項目...
    七寸知架構(gòu)閱讀 2,537評論 0 53
  • 介紹 迭代器模式在我們?nèi)粘>幊讨惺褂梅浅nl繁饲宛,像list、map嗜价、數(shù)組等都會用到迭代器模式艇抠。迭代器模式屬于行為型模...
    東西的南北閱讀 226評論 0 1
  • 前言 Android的設(shè)計模式系列文章介紹,歡迎關(guān)注炭剪,持續(xù)更新中: Android的設(shè)計模式-設(shè)計模式的六大原則一...
    四月葡萄閱讀 3,652評論 0 4
  • 剛打開門進(jìn)去,看見室友猶如一條咸魚橫躺在沙發(fā)上翔脱,身上蓋著的毛巾被一大半塌在沙發(fā)底下奴拦。捧著手機(jī),手指時不時點擊一...
    十克閱讀 284評論 0 2