一塊兒學設計模式 - 迭代器模式

迭代器模式(Iterator Pattern)屬於行爲型模式的一種,提供一種方法訪問一個容器中各個元素,而又不須要暴露該對象的內部細節。java

<!-- more -->git

概述

迭代器模式聽起來可能感受很陌生,可是實際上,迭代器模式是全部設計模式中最簡單也是最經常使用的設計模式,正是由於太經常使用了,因此致使不少人忽略了它的存在。spring

在實際的開發過程當中,咱們可能須要針對不一樣的需求,可能須要以不一樣的方式來遍歷整個整合對象,但又不但願在聚合對象的抽象接口層中充斥着各類不一樣的便利操做。這個時候咱們就須要這樣一種東西,它應該具有以下三個功能:設計模式

  • 可以便利一個聚合對象。
  • 咱們不須要了解聚合對象的內部結構。
  • 可以提供多種不一樣的遍歷方式。

迭代器模式: 把在元素之間遊走的責任交給迭代器,而不是聚合對象。簡化了聚合的接口和實現,讓聚合更專一在它所應該專一的事情上,這樣作更加符合單一責任原則。springboot

UML結構圖微信

迭代器模式UML結構圖

模式結構微信公衆平臺

  • Iterator(抽象迭代器): 具體迭代器須要實現的接口,提供了遊走聚合對象元素之間的方法
  • ConcreteIterator(具體迭代器): 對具體的聚合對象進行遍歷,每個聚合對象都應該對應一個具體的迭代器。
  • Aggregate(抽象聚合類): 存儲和管理元素對象,聲明瞭createIterator()用於建立一個迭代器對象,充當抽象迭代器工廠角色
  • ConcreteAggregate(具體聚合類): 實現了在抽象聚合類中聲明的createIterator(),該方法返回一個與該具體聚合類對應的具體迭代器ConcreteIterator實例。

案例

UML圖以下:ide

UML圖

1.先定義抽象迭代器``測試

interface Iterator<E> {

    //判斷是否有還有下一個元素
    boolean hasNext();

    //取出下一個對象
    E next();
}

2.在定義一個具體迭代器對象,實現了Iterator中的方法ui

class MusicIterator<E> implements Iterator<E> {

    private E[] es;
    private int position = 0;

    public MusicIterator(E[] es) {
        this.es = es;
    }

    @Override
    public boolean hasNext() {
        return position != es.length;
    }

    @Override
    public E next() {
        E e = es[position];
        position += 1;
        return e;
    }
}

3.接下來定義抽象聚合類(常爲 Collection , List , Set 等)

interface AbstractList<E> {

    void add(E e);

    Iterator<E> createIterator();
}

4.最後建立具體聚合類 (常爲 ArrayList , HashSet 等,是抽象聚合類的實現類)

class MusicList implements AbstractList<String> {

    private String[] books = new String[5];
    private int position = 0;


    @Override
    public void add(String name) {
        books[position] = name;
        position += 1;
    }

    @Override
    public Iterator<String> createIterator() {
        return new MusicIterator<>(books);
    }
}

5.建立測試工程

public class Client {

    public static void main(String[] args) {
        AbstractList<String> list = new MusicList();
        list.add("涼涼");
        list.add("奇談");
        list.add("紅顏");
        list.add("伴虎");
        list.add("在人間");
        Iterator<String> iterator = list.createIterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }

}

6.運行效果

涼涼
奇談
紅顏
伴虎
在人間

至此一個簡單的迭代器就完成了,實現了對容器的遍歷。能夠看到迭代器在Client中遍歷時根本不須要知道他底層的實現,只須要經過迭代器來遍歷就能夠了。

JDK中應用

上文介紹了本身實現一個簡單的迭代器,看完的應該就不陌生了,它其實在JAVA的不少集合類中被普遍應用,接下來看看JAVA源碼中是如何使用迭代器模式的。

List<String> list = new ArrayList<>();
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

看完這段代碼是否是很熟悉,與咱們上文代碼基本相似

  • List:抽象聚合器
  • ArrayList:具體的聚合類
  • Iterator:抽象迭代器
  • list.iterator():返回的是實現了Iterator接口的具體迭代器對象

接下來看下ArrayList中的iterator是如何實現的

public Iterator<E> iterator() {
    return new Itr();
}

private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    public boolean hasNext() {
        return cursor != size;
    }

    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }

    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public void forEachRemaining(Consumer<? super E> consumer) {
        Objects.requireNonNull(consumer);
        final int size = ArrayList.this.size;
        int i = cursor;
        if (i >= size) {
            return;
        }
        final Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length) {
            throw new ConcurrentModificationException();
        }
        while (i != size && modCount == expectedModCount) {
            consumer.accept((E) elementData[i++]);
        }
        // update once at end of iteration to reduce heap write traffic
        cursor = i;
        lastRet = i - 1;
        checkForComodification();
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

這部分代碼仍是比較簡單,大體就是在iterator方法中返回了一個實例化的Iterator對象。Itr是一個內部類,它實現了Iterator接口並實現了其中的方法。

當咱們在使用JAVA開發的時候,想使用迭代器模式的話,只要讓咱們本身定義的容器類實現java.util.Iterable並實現其中的iterator方法使其返回一個java.util.Iterator的實現類就能夠了。

總結

迭代器模式與集合共生共死的,通常來講,咱們只要實現一個集合,就須要同時提供這個集合的迭代器,就像java中的Collection,List、Set、Map等,這些集合都有本身的迭代器。假如咱們要實現一個這樣的新的容器,固然也須要引入迭代器模式,給咱們的容器實現一個迭代器。但在絕大多數狀況,咱們是不須要本身實現的,基本都內置了

優勢

  • 支持不一樣方式遍歷一個聚合對象,在同一個聚合上能夠有多個遍歷。
  • 簡化了聚合類。因爲引入了迭代器,在原有的聚合對象中不須要再自行提供數據遍歷等方法。
  • 在迭代器模式中,因爲引入了抽象層,增長新的聚合類和迭代器類都很方便,無須修改原有代碼

缺點

  • 因爲迭代器模式將存儲數據和遍歷數據的職責分離,增長新的聚合類須要對應增長新的迭代器類,類的個數成對增長,這在必定程度上增長了系統的複雜性。
  • 抽象迭代器的設計難度較大,須要充分考慮到系統未來的擴展,例如JDK內置迭代器Iterator就沒法實現逆向遍歷,若是須要實現逆向遍歷,只能經過其子類ListIterator等來實現,而ListIterator迭代器沒法用於操做Set類型的聚合對象。在自定義迭代器時,建立一個考慮全面的抽象迭代器並非件很容易的事情。

說點什麼

全文代碼:https://gitee.com/battcn/design-pattern/tree/master/Chapter14/battcn-iterator

  • 我的QQ:1837307557
  • battcn開源羣(適合新手):391619659

微信公衆號:battcn(歡迎調戲)

福利

關注公衆號:battcn,回覆springboot便可得到 <Spring Boot從入門到實戰 基礎實戰系列教程全集><2017最新spring boot 外賣實戰微信公衆平臺視頻教程>

相關文章
相關標籤/搜索