軟件設計模式學習(二十)迭代器模式


迭代器模式是一種使用頻率很是高的設計模式,迭代器用於對一個聚合對象進行遍歷。經過引入迭代器能夠將數據的遍歷功能從聚合對象中分離出來,聚合對象只負責存儲數據,聚合對象只負責存儲數據,而遍歷數據由迭代器來完成。java


模式動機

一個聚合對象,如一個列表(List)或者一個集合(Set),應該提供一種方法來讓別人能夠訪問它的元素,而又不須要暴露它的內部結構。此外,針對不一樣的須要,可能還要以不一樣方式遍歷整個聚合對象,可是咱們不希在聚合對象的抽象層接口中充斥着各類不一樣遍歷的操做。怎樣遍歷一個聚合對象,又不須要了解聚合對象的內部結構,還能提供多種不一樣的遍歷方式,這就是迭代器模式所要解決的問題。算法

迭代器模式中,提供一個外部的迭代器來對聚合對象進行訪問和遍歷,迭代器定義一個訪問該聚合元素的接口,而且能夠跟蹤當前遍歷對象,瞭解哪些元素已經遍歷過而哪些沒有。設計模式


模式定義

提供一種方法來訪問聚合對象,而不用暴露這個對象的內部表示,其別名爲遊標(Cursor)。迭代器模式是一種對象行爲模式。數組


模式結構

  1. Iterator(抽象迭代器)框架

    抽象迭代器定義了訪問和遍歷元素的接口,通常聲明如下方法:this

    • 用於獲取第一個元素的 first()
    • 用於訪問下一個元素的 next()
    • 用於判斷是否還有下一個元素的 hasNext()
    • 用於獲取當前元素的 currentItem()
  2. ConcreteIterator(具體迭代器)設計

    具體迭代器實現了抽象迭代器接口,完成對聚合對象的遍歷,同時在對聚合進行遍歷時跟蹤其當前位置code

  3. Aggregate(抽象聚合類)對象

    抽象聚合類用於存儲對象,並定義建立相應迭代器對象的接口,聲明一個 createIterator() 方法用於建立一個迭代器對象blog

  4. ConcreteAggregate(具體聚合類)

    具體聚合類實現了建立相應迭代器的接口,實現了在聚合類中聲明的 createIterator() 方法,該方法返回一個與具體聚合對應的具體迭代器 ConcreteIterator 實例


模式分析

存儲數據是聚合對象的最基本職責,其中包含存儲數據的類型、存儲空間的大小、存儲空間的分配,以及存儲的方式和順序。然而,聚合對象除了能存儲數據外,還必須提供遍歷訪問其內部數據的方式,同時這些遍歷方式可能會根據不一樣的情形提供不一樣的實現。

所以,聚合對象主要有兩個職責:一是存儲內部數據;二是遍歷內部數據。前者是聚合對象的基本功能,後者是能夠分離的。根據單一職責原則,對象承擔的職責越少,對象的穩定性就越好,咱們將遍歷聚合對象中數據的行爲提取出來,封裝到一個迭代器中,經過專門的迭代器來遍歷聚合對象的內部數據。迭代器模式是單一職責原則的完美體現。

下面經過一個簡單的自定義迭代器來分析迭代器模式的結構

首先定義一個簡單的迭代器去接口

public interface MyIterator {
    void first();	// 訪問第一個元素
    void next();	// 訪問下一個元素
    boolean isLast();	// 判斷是不是最後一個元素
    Object currentItem();	// 獲取當前元素
}

而後須要定義一個聚合接口

public interface MyCollection {
    // 返回一個 MyIterator 迭代器對象
    MyIterator createIterator();
}

定義好抽象層以後,咱們須要定義抽象迭代器接口和抽象聚合接口的實現類,通常將具體迭代器類做爲具體聚合類的內部類,從而迭代器能夠實現直接訪問聚合類中的數據

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 類實現了 MyCollection 接口,實現了 createIterator() 方法,同時定義了一個數組用於存儲數據元素,還定義了一個實現了 MyIterator 接口的內部類,索引變量 currentIndex 用於保存所操做的數組元素的下標值。客戶端代碼以下:

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);
    }
}

除了使用內部類實現以外,也可使用常規的方式來實現迭代器

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);
    }
}

迭代器模式中應用了工廠方法模式,聚合類充當工廠類,而迭代器充當產品類


模式優缺點

迭代器模式優勢:

  1. 支持以不一樣的方式遍歷一個聚合對象。在迭代器模式中只須要用一個不一樣的迭代器來替換原有迭代器便可改變遍歷算法,也能夠本身定義迭代器的子類以支持新的遍歷方式。
  2. 迭代器簡化了聚合類。原有聚合對象再也不須要自行提供遍歷數據等操做方法。
  3. 在同一個聚合上能夠有多個遍歷。因爲每一個迭代器都保持本身的遍歷狀態,所以能夠對一個聚合對象進行多個遍歷操做。
  4. 增長新的聚合類和迭代器類都很方便,無須修改原代碼,知足開閉原則。

迭代器模式缺點:

  1. 因爲迭代器模式將存儲數據和遍歷數據的職責分離,增長新的聚合類須要對應增長新的迭代器類,類的個數成對增長,在必定程度上增長了系統的複雜性

模式適用環境

在如下狀況可使用迭代器模式:

  1. 訪問一個聚合對象的內容而無須暴露它的內部表示
  2. 須要爲聚合對象提供多種遍歷方式
  3. 爲遍歷不一樣的聚合結構提供一個統一的接口

Java 迭代器

Java 中的集合框架 Collections,其基本接口層次結構如圖

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 的子類,它們構成了 Java 語言對迭代器模式的支持。

在 JDK 中,Iterator 接口具備以下三個基本方法:

  1. Object next():經過反覆調用 next() 方法能夠逐個訪問聚合中的元素
  2. boolean hasNext():用於判斷聚合對象在是否還存在下一個元素,爲了避免拋出異常,必須在調用 next() 以前先調用 hasNext()。若是迭代對象仍然擁有可供訪問的元素,那麼 hasNext() 返回 true
  3. void remove():刪除上一次調用 next() 時返回的元素

Java 迭代器能夠理解爲它工做時在聚合對象的各個元素之間,每調用一次 next() 方法,迭代器便越過下個元素,而且返回它剛越過的那個元素的地址引用。

在第一個 next() 方法被調用時,迭代器由「元素1」與「元素2」之間移動至「元素2」與「元素3」之間,跨越了「元素2」,所以 next() 方法將返回對「元素2」的引用;在第二個 next() 方法被調用時,迭代器由「元素2」與「元素3」之間移至「元素3」與「元素4」之間,next() 方法將返回對「元素3」的引用,此時調用 remove() 方法,則可將」元素3「刪除。

須要注意的是,next() 方法與 remove() 方法的調用是相互關聯的。若是調用 remove() 以前沒有先對 next() 進行調用,那麼將拋出異常,由於沒有任何可供刪除的元素

相關文章
相關標籤/搜索