1. 程式人生 > >java設計模式——迭代器模式(Iterator Pattern)

java設計模式——迭代器模式(Iterator Pattern)

概述:          在軟體開發中,我們經常需要使用聚合物件來儲存一系列資料。聚合物件擁有兩個職責:一是儲存資料;二是遍歷資料。從依賴性來看,前者是聚合物件的基本職責;而後者既是可變化的,又是可分離的。因此,可以將遍歷資料的行為從聚合物件中分離出來,封裝在一個被稱之為“迭代器”的物件中,由迭代器來提供遍歷聚合物件內部資料的行為,這將簡化聚合物件的設計,更符合“單一職責原則”的要求。 定義:提供一種方法來訪問聚合物件,而不用暴露這個物件的內部表示,其別名為遊標(Cursor)。迭代器模式是一種物件行為型模式。
結構:
  •  Iterator(抽象迭代器):它定義了訪問和遍歷元素的介面,聲明瞭用於遍歷資料元素的方法,例如:用於獲取第一個元素的first()方法,用於訪問下一個元素的next()方法,用於判斷是否還有下一個元素的hasNext()方法,用於獲取當前元素的currentItem()方法等,在具體迭代器中將實現這些方法。
  • ConcreteIterator(具體迭代器):它實現了抽象迭代器介面,完成對聚合物件的遍歷,同時在具體迭代器中通過遊標來記錄在聚合物件中所處的當前位置,在具體實現時,遊標通常是一個表示位置的非負整數。
  • Aggregate(抽象聚合類):它用於儲存和管理元素物件,宣告一個createIterator()方法用於建立一個迭代器物件,充當抽象迭代器工廠角色。
  • ConcreteAggregate(具體聚合類):它實現了在抽象聚合類中宣告的createIterator()方法,該方法返回一個與該具體聚合類對應的具體迭代器ConcreteIterator例項。

UML圖:


場景:現在有一個圖書館,需要遍歷其中的書籍。圖書館就是一個聚合物件。
package com.example.iteratorpattern;

import java.util.ArrayList;
import 
java.util.List;
import java.util.Objects;

/**
 * Created by **
 *  聚合類
  */

public abstract class AbstractBookList {
    protected List<Object>  objects new ArrayList<Object>();

    public AbstractBookList(List objects){
        this. objects = objects;
    }

    protected void addObject(Object object){
        this. objects.add(object);
    }

    protected void removeObject(Object object){
        this. objects.remove(object);
    }

    protected List getObjects(){
        return this. objects;
    }

    protected abstract BookIterator createIterator();
}

package com.example.iteratorpattern;

import java.util.List;

/**
 * Created by **
 */

public class BookList  extends AbstractBookList {

    public BookList(List objects) {
        super(objects);
    }

    @Override
    protected BookIterator createIterator() {
        return new BookIterator( this);
    }
}

package com.example.iteratorpattern;

/**
 * Created by *8
 */

public interface AbstractIterator {
    /**
     *  移動至上一個元素
      */
    void previous();

    /**
     *  移動至下一個元素
      */
    void next();

    /**
     *  判斷是否為第一個元素
      @return
      */
    boolean isFirst();

    /**
     *  判斷是否為最後一個元素
      @return
      */
    boolean isLast();

    /**
     *  獲取上一個元素
      */
    Object getNextItem();
    /**
     *  獲取下一個元素
      */
    Object getPrevious();

}

package com.example.iteratorpattern;

import java.util.List;

/**
 * Created by **
 */

public class BookIterator  implements AbstractIterator {

    private BookList  bookList;
    private List  list;
    private int  cursor1;
    private int  cursor2;

    public BookIterator(BookList bookList){
        this. bookList = bookList;
        this. list = bookList.getObjects();
        cursor1 0;
        cursor2 this. list.size()- 1;
    }
    @Override
    public void previous() {
        if ( cursor2 > - 1) {
            cursor2 --;
        }
    }

    @Override
    public void next() {
        if ( cursor1< this. list.size()){
            cursor1 ++;
        }
    }

    @Override
    public boolean isFirst() {
        return  cursor2 == - 1;
    }

    @Override
    public boolean isLast() {
        return  cursor1 ==  this. list.size();
    }

    @Override
    public Object getNextItem() {
        return this. list.get( cursor1);
    }

    @Override
    public Object getPrevious() {
        return this. list.get( cursor2);
    }
}

客戶端: List objects =  new ArrayList<>();
    objects.add( " 阿甘正傳 ");
    objects.add( " 西遊記 ");
    objects.add( "java 程式設計思想 ");
    objects.add( " 設計模式 ");

    AbstractBookList bookList;
    AbstractIterator iterator;

    bookList =  new BookList(objects);
    iterator = bookList.createIterator();

    logcat( " 正序遍歷 ");
    logcat( "first list object = "+objects.get( 1));
    while (!iterator.isLast()){
        logcat(iterator.getNextItem()+ ",");
        iterator.next();
    }
    logcat( " 後序遍歷 ");
    while (!iterator.isFirst()){
        logcat(iterator.getPrevious()+ ",");
        iterator.previous();
    }
}
private void logcat(String msg){
    Log. d( "test",msg);
}

log輸出: 12-16 17:48:49.915 11468-11468/? D/test: 正序遍歷 12-16 17:48:49.915 11468-11468/? D/test: first list object = 西遊記 12-16 17:48:49.915 11468-11468/? D/test: 阿甘正傳, 12-16 17:48:49.915 11468-11468/? D/test: 西遊記, 12-16 17:48:49.915 11468-11468/? D/test: java程式設計思想, 12-16 17:48:49.915 11468-11468/? D/test: 設計模式, 12-16 17:48:49.915 11468-11468/? D/test: 後序遍歷 12-16 17:48:49.915 11468-11468/? D/test: 設計模式, 12-16 17:48:49.915 11468-11468/? D/test: java程式設計思想, 12-16 17:48:49.915 11468-11468/? D/test: 西遊記, 12-16 17:48:49.915 11468-11468/? D/test: 阿甘正傳,
優點:
  • 它支援以不同的方式遍歷一個聚合物件,在同一個聚合物件上可以定義多種遍歷方式。在迭代器模式中只需要用一個不同的迭代器來替換原有迭代器即可改變遍歷演算法,我們也可以自己定義迭代器的子類以支援新的遍歷方式。
  • 迭代器簡化了聚合類。由於引入了迭代器,在原有的聚合物件中不需要再自行提供資料遍歷等方法,這樣可以簡化聚合類的設計。
  • 在迭代器模式中,由於引入了抽象層,增加新的聚合類和迭代器類都很方便,無須修改原有程式碼,滿足“開閉原則”的要求。

缺點:
  • 由於迭代器模式將儲存資料和遍歷資料的職責分離,增加新的聚合類需要對應增加新的迭代器類,類的個數成對增加,這在一定程度上增加了系統的複雜性。
  • 抽象迭代器的設計難度較大,需要充分考慮到系統將來的擴充套件,例如JDK內建迭代器Iterator就無法實現逆向遍歷,如果需要實現逆向遍歷,只能通過其子類ListIterator等來實現,而ListIterator迭代器無法用於操作Set型別的聚合物件。在自定義迭代器時,建立一個考慮全面的抽象迭代器並不是件很容易的事情。

適用場景:
  • 訪問一個聚合物件的內容而無須暴露它的內部表示。將聚合物件的訪問與內部資料的儲存分離,使得訪問聚合物件時無須瞭解其內部實現細節。
  • 需要為一個聚合物件提供多種遍歷方式。
  • 為遍歷不同的聚合結構提供一個統一的介面,在該介面的實現類中為不同的聚合結構提供不同的遍歷方式,而客戶端可以一致性地操作該介面。

拓展:       現有的jdk裡邊已經有iterable介面了,裡邊只有一個方法iterator(),通過該方法可以遍歷聚集類的屬性和方法,現在有該介面或者實現的類有:
所以我們可以直接適用現成的迭代器就行了,不需要自己去定義。
所以上面客戶端的程式碼可以做如下修改: List objects =  new ArrayList<>();
objects.add( " 阿甘正傳 ");
objects.add( " 西遊記 ");
objects.add( "java 程式設計思想 ");
objects.add( " 設計模式 ");

logcat( " 正序遍歷 ");
ListIterator listIterator = objects.listIterator();
while (listIterator.hasNext()){
    Object next = listIterator.next();
    logcat(next+ " ");
}
logcat( " 後序遍歷 ");
while (listIterator.hasPrevious()){
    Object previous = listIterator.previous();
    logcat(previous+ " ");
}

log輸出: 12-16 18:02:01.095 13345-13345/? D/test: 正序遍歷 12-16 18:02:01.095 13345-13345/? D/test: 阿甘正傳 12-16 18:02:01.095 13345-13345/? D/test: 西遊記 12-16 18:02:01.095 13345-13345/? D/test: java程式設計思想 12-16 18:02:01.095 13345-13345/? D/test: 設計模式 12-16 18:02:01.095 13345-13345/? D/test: 後序遍歷 12-16 18:02:01.095 13345-13345/? D/test: 設計模式 12-16 18:02:01.095 13345-13345/? D/test: java程式設計思想 12-16 18:02:01.095 13345-13345/? D/test: 西遊記 12-16 18:02:01.095 13345-13345/? D/test: 阿甘正傳