Java設計模式系列之——迭代器模式

本篇我們採取倒敘的手法來講解迭代器模式,先看下面一段我們在平時工作中常見的代碼:

package com.mazhichu.designpatterns.iterator;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 * <p class="detail">
 * 功能: 前言demo
 * </p>
 *
 * @author Moore
 * @ClassName Preface demo.
 * @Version V1.0.
 * @date 2019.11.22 10:27:32
 */
public class PrefaceDemo {

 public static void main(String[] args) {
 List<String> strList = Arrays.asList("碼","之","初","在","努","力");

 Iterator iterator = strList.iterator();

 while (iterator.hasNext()){
 System.out.println(iterator.next());
 }
 }
}

看一下運行結果:

Java設計模式十八(迭代器模式)

 

有人要疑問了,這不就是個簡單的遍歷List集合嗎,有什麼值得拿出來說的?是的,這就是個簡單的遍歷,也是我們開發過程中最常見的遍歷方式,但是在設計模式裏,它就是陪在我們身邊的最熟悉的陌生人,因爲它就是迭代器模式。什麼,迭代器模式就是遍歷?對,迭代器模式就是遍歷...

十七年前

Java設計模式十八(迭代器模式)

 

如上圖,讓時間回到十八年前,在手機風靡之前,它可是最受年輕人歡迎、王者一樣的存在,那時候只要誰帶一個隨身聽或者是mp3在身上,那就是走在時尚前沿的人,上課時偷偷放在課桌裏,耳機從衣服裏穿過,從脖子後面繞到耳朵裏戴着偷偷地聽,回到宿舍和好兄弟或者好姐妹兩個人一人一個耳機,睡在一個被窩裏偷偷的聽。那個年代,男生的隨身聽裏誰沒有幾首四大天王、Beyond的歌,女生的mp3裏誰沒有幾首劉若英、張韶涵等。那個青澀的年代,那段美好的時光,荏苒。

往事不可追,回憶彷彿冷風吹。讓我們回到mp3身上,我們都知道打開mp3按播放鍵就能聽歌,但是我們並不知道爲什麼把歌下載到內存卡里mp3就能播放,內存卡怎麼存放歌曲的,我們也無需關心。

Java設計模式十八(迭代器模式)

 

抽象點來說,每首歌都是類型相同的成員對象,而存儲歌曲的內存卡、磁帶、光碟等就是存儲這些成員對象的聚合類(Aggregate Classes),其對象稱爲聚合對象。其主要有兩個職責,一個是存儲數據,另一個是遍歷數據。存儲數據是聚合對象的基本職責要求,也就是必備的。但是遍歷數據是可以變化的,變化意味着可分離,也就是將遍歷數據的行爲從聚合對象中抽象分離出來,封裝在其他對象裏,由其他對象去管理遍歷這個行爲,這裏的其他的對象就是“迭代器”,通過迭代器去遍歷聚合對象的內部數據。

試想一下,如果內存卡沒有mp3、手機等播放媒介,磁帶沒有隨身聽、復讀機,光盤沒有電腦、DVD等,那廠家在生產內存卡、磁帶、光盤的時候,是不是既要有存儲的功能,也要有能遍歷它們然後播放的功能?

從設計模式的原則上來說,這種簡化聚合對象的設計,符合“單一職責原則”,同時也符合“開閉原則”。
至此,我們來正式講解迭代器模式。


什麼是迭代器模式

Iterator pattern:Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

迭代器模式:提供一種方法訪問一個容器對象中各個元素,而又不暴露該對象的內部細節。它是一種行爲型模式。


現在我們應該能明白,內存卡、磁帶、光盤這些是聚合對象,mp3、隨身聽、DVD這些就是爲聚合對象提供的迭代器對象,用戶不用知道聚合對象的內部結構,就可以通過迭代器來遍歷聚合對象(通過mp3來遍歷讀取內存卡里面的歌曲並播放),同時可以自己自定義遍歷方式,擴展迭代器功能。

迭代器模式的四個角色

  • Iterator(抽象迭代器):它定義了訪問和遍歷元素的接口,聲明瞭用於遍歷數據元素的方法
  • ConcreteIterator(具體迭代器):它實現了抽象迭代器接口,完成對具體聚合對象的遍歷,同時在具體迭代器中通過遊標來記錄在聚合對象中遍歷時所處的當前位置。每一個聚合對象都應該對應一個具體的迭代器。
  • Aggregate(抽象聚合類):用於存儲和管理元素對象,聲明一個 createIterator() 方法用於創建一個迭代器對象,充當抽象迭代器工廠角色。
  • ConcreteAggregate(具體聚合類):實現了在抽象聚合類中聲明的 createIterator() 方法,該方法返回一個與該具體聚合類對應的具體迭代器 ConcreteIterator 實例。

迭代器模式UML

Java設計模式十八(迭代器模式)

 

代碼實例

1、編寫抽象迭代器角色

package com.mazhichu.designpatterns.iterator;

/**
 * <p class="detail">
 * 功能: 抽象迭代器角色
 * </p>
 *
 * @param <E> the type parameter
 * @author Moore
 * @ClassName Iterator.
 * @Version V1.0.
 * @date 2019.11.25 17:24:43
 */
public interface Iterator<E> {

 /**
 * 判斷是否有下一個元素
 *
 * @return
 * @author Moore
 * @date 2019.11.25 17:24:43
 */
 boolean hasNext();

 /**
 * 取出下一個元素
 *
 * @return
 * @author Moore
 * @date 2019.11.25 17:24:43
 */
 E next();
}

2、編寫具體迭代器角色

package com.mazhichu.designpatterns.iterator;

/**
 * <p class="detail">
 * 功能: 具體迭代器角色(MP3)
 * </p>
 *
 * @param <E> the type parameter
 * @author Moore
 * @ClassName Concrete iterator mp 3.
 * @Version V1.0.
 * @date 2019.11.25 17:25:03
 */
public class ConcreteIteratorMP3<E> implements Iterator<E> {

 private E[] elements;

 private int cursor = 0;

 /**
 * Instantiates a new Concrete iterator mp 3.
 *
 * @param elements the elements
 */
 public ConcreteIteratorMP3(E[] elements) {
 this.elements = elements;
 }

 /**
 * 判斷是否有下一個元素
 *
 * @return
 */
 @Override
 public boolean hasNext() {
 return cursor != elements.length;
 }

 /**
 * 取出下一個元素
 *
 * @return
 */
 @Override
 public E next() {
 E e = null;
 if(this.hasNext()){
 e = elements[cursor];
 cursor ++;
 }
 return e;
 }
}

3、編寫抽象聚合類角色

package com.mazhichu.designpatterns.iterator;

/**
 * <p class="detail">
 * 功能: 抽象聚合類角色
 * </p>
 *
 * @param <E> the type parameter
 * @author Moore
 * @ClassName Aggregate.
 * @Version V1.0.
 * @date 2019.11.25 17:23:37
 */
public interface Aggregate<E> {
 /**
 * <p class="detail">
 * 功能: 創建一個迭代器
 * </p>
 *
 * @return iterator
 * @author Moore
 * @date 2019.11.25 17:23:37
 */
 Iterator<E> createIterator();

 /**
 * <p class="detail">
 * 功能: 添加一個元素
 * </p>
 *
 * @param e :
 * @author Moore
 * @date 2019.11.25 17:23:37
 */
 void add(E e);
}

4、編寫具體聚合類角色

package com.mazhichu.designpatterns.iterator;

public class Test {
 public static void main(String[] args) {
     Aggregate memoryCard = new ConcreteAggregateMemoryCard();
     memoryCard.add("17歲");
     memoryCard.add("像我這樣的人");
     memoryCard.add("那些花兒");
     memoryCard.add("平凡之路");
     memoryCard.add("千千闕歌");
     memoryCard.add("玻璃之情");
     Iterator<String> mp3 = memoryCard.createIterator();
     while(mp3.hasNext()){
     String song = mp3.next();
     System.out.println(song);
    }
 }
}

5、客戶端測試

package com.mazhichu.designpatterns.iterator;

public class Test {
 public static void main(String[] args) {
 Aggregate memoryCard = new ConcreteAggregateMemoryCard();
 memoryCard.add("17歲");
 memoryCard.add("像我這樣的人");
 memoryCard.add("那些花兒");
 memoryCard.add("平凡之路");
 memoryCard.add("千千闕歌");
 memoryCard.add("玻璃之情");
 Iterator<String> mp3 = memoryCard.createIterator();
 while(mp3.hasNext()){
 String song = mp3.next();
 System.out.println(song);
 }
 }
}


6、查看運行結果

Java設計模式十八(迭代器模式)

 


通過代碼,可以看出來迭代器mp3並不知道也不關心內存卡的內部結構和具體實現,只是將內存卡里面的歌曲一個個遍歷出來,這就是一個簡單的迭代器模式。

現在如果回頭看文章開始的代碼,是不是能立馬意識到在運用迭代器模式呢,List就是一個抽象聚合類,ArrayList就是具體的聚合類,Iterator就是迭代器,是不是很清楚了。

Java設計模式十八(迭代器模式)

 

總結

優點

  • 它支持以不同的方式遍歷一個聚合對象,在同一個聚合對象上可以定義多種遍歷方式。在迭代器模式中只需要用一個不同的迭代器來替換原有迭代器即可改變遍歷算法,我們也可以自己定義迭代器的子類以支持新的遍歷方式。
  • 使用迭代器之後,聚合類只用負責存儲,滿足“單一職責原則”。
  • 在迭代器模式中,由於引入了抽象層,增加新的聚合類和迭代器類都很方便,無須修改原有代碼,滿足“開閉原則”。

缺點

  • 存儲數據和遍歷數據的職責分離,增加新的聚合類需要對應增加新的迭代器類,類的個數成對增加,增加了系統的複雜性。


如果你看過JDK源碼,就會發現,我們自己寫的Aggregate跟Collection,List,Set、Map等差不多,或者說是它們的簡化版,通常一個集合伴隨這一個迭代器,既然JDK已經幫我們造好了輪子,就不建議自己再去爲一個集合寫一個迭代器了,站在巨人的肩膀上才能看的更遠,走的更穩。本篇只是爲了幫助大家理解迭代器模式。

 

文章同步公衆號:碼之初,每天推送Java技術文章,期待您的關注!

原創不易,轉載請註明出處,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章