設計模式——迭代器(Iterator)模式
- 概述
迭代器模式簡單的說(按我目前的理解)就是一個類提供一個對外迭代的介面,方面呼叫者迭代。這個迭代介面至少包括兩個方法:hasNext()--用於判斷是否還有下一個,next()--用於取出下一個物件(或值)。而外部使用這個類(取出這個類中的物件或值)時,不用關心這個類儲存物件或資料的具體資料結構,即使這個類的儲存資料結構臨時發生改變,呼叫者不作任何程式碼修改仍然可以正常工作。從而實現程式碼的可重用性和低耦合性。下面以例項說明。
- 例項
假設有兩個書架(BookShelf),一個書架用陣列Array的形式存放書,另一個書架用ArrayList的形式存放書,兩個書架可以存放多本書(Book)的名字,Main方法通過書架取出書名打印出來。
傳統模式:很容易想到第一個書架通過一個方法返回存放書的陣列,另一個書架返回存放書的ArrayList。就像下面這樣:
//傳統模式: public Book[] getBooks(){ return books; }
//普通模式 public ArrayList<Book> getBooks(){ return books; }
這樣的缺點是什麼呢?缺點在於當我們同時獲取到這兩個書架時,迭代輸出的方式就有所不同,因為一個是陣列,一個是List,這個很容易想到吧,可能你覺得很簡單,但是如果有十個,有幾十個用不同的資料結構儲存呢,迭代的方式又要改。並且,如果我開始用的陣列存放書,後來我又不想用陣列而改用List存放呢?那豈不是既要修改存放書的程式碼的同時又要修改迭代輸出的程式碼?Oh,No..太麻煩了吧。可能你會想自己寫的程式碼,幹嘛每個書架存放書的資料結構要不同呢?那如果不是自己寫的呢?比如我現在要合併多個餐館的選單為一個選單並輸出列印,而這些選單的儲存方式肯定不同,很有可能這幾個餐館的程式碼都不是同一個人寫的。這時候我們再去一個一個迴圈未免也太麻煩。
使用迭代器模式:既然我不同的書架存放書的資料結構不同,那我可以每個書架對外提供一個迭代介面,而每個介面都包含有hasNext方法和Next方法。這時在外部迴圈迭代輸出時,就不用擔心每個書架的書是怎樣存放的,我只需要能通過Next方法取出書就可以了,就像下面這樣:
陣列存放:
public class BookShelf { private Book[] books; private int last = 0; public BookShelf(int maxsize) { this.books = new Book[maxsize]; }public Book getBookAt(int index){ return books[index]; } public void appendBook(Book book){ this.books[last] = book; last++; } public int getLength(){ return last; } // //傳統模式: // public Book[] getBooks(){ // return books; // } public Iterator iterator() { // return new BookShelfIterator(this); return new BookShelfIterator(); } class BookShelfIterator implements Iterator{ // private BookShelf bookShelf; //採用內部類,可以直接呼叫外部類方法,不用新增引用。 private int index ; public BookShelfIterator() { // this.bookShelf=bookShelf; this.index = 0; } @Override public boolean hasNext() { if (index < getLength()){ return true; }else { return false; } } @Override public Object next() { Book book = getBookAt(index); index++; return book; } } }
ArrayList存放:
import java.util.ArrayList; public class BookShelf1 { private ArrayList<Book> books ;//使用ArrayList實現 public BookShelf1(int maxsize) { this.books = new ArrayList<>(maxsize);//初始大小 } public Book getBookAt(int index){ return books.get(index); } public void appendBook(Book book){ this.books.add(book); } public int getLength(){ return books.size(); } public Iterator iterator() { return new BookShelfIterator(); } // //普通模式 //// public ArrayList<Book> getBooks(){ //// return books; //// } class BookShelfIterator implements Iterator{ // private BookShelf bookShelf; //採用內部類,可以直接呼叫外部類方法,不用新增引用。 private int index ; public BookShelfIterator() { // this.bookShelf=bookShelf; this.index = 0; } @Override public boolean hasNext() { if (index < getLength()){ return true; }else { return false; } } @Override public Object next() { Book book = getBookAt(index); index++; return book; } } }
而我們獲取這兩個書架的書時,只需要呼叫兩個書架的iterator()方法就可以獲取到同樣的Iterator物件,這個物件中都包含兩個相同的方法hasNext()和Next(),這樣我們就很方便的迭代輸出了,並且我們不用關心每個書架裡面是通過陣列還是list存放書的。就像下面這樣:
ArrayList<Iterator> iterList = new ArrayList<>(); //存放Iterator BookShelf bookShelf = new BookShelf(4);//例項化第一個書架 bookShelf.appendBook(new Book("Around the World in 80 Days")); bookShelf.appendBook(new Book("Bible")); bookShelf.appendBook(new Book("Daddy-Long-Legs")); bookShelf.appendBook(new Book("Cinderella")); Iterator it = bookShelf.iterator(); iterList.add(it); BookShelf1 bookShelf1 = new BookShelf1(4);//例項化第二個書架 bookShelf1.appendBook(new Book("Around the World in 80 Days__")); bookShelf1.appendBook(new Book("Bible__")); bookShelf1.appendBook(new Book("Daddy-Long-Legs__")); bookShelf1.appendBook(new Book("Cinderella__")); Iterator it1 = bookShelf1.iterator(); iterList.add(it1); for (int i = 0; i < iterList.size(); i++) { Iterator iterator = iterList.get(i); while (iterator.hasNext()){ Book book = (Book) iterator.next(); System.out.println(book.getName()); } }
- 完整程式碼
請移步:https://github.com/yyc007/DesignPatterns/
- 小結
使用迭代器模式,可以幫助我們編寫可以複用的類,當這個類發生改變時,不需要對其它的類進行修改或者很小的修改即可應對。就上面的書架例子來說,不管BookShelf如何變化,只要BookShelf返回的Iterator類的例項沒有問題(hasNext方法和Next方法都可以正常工作),即使呼叫方不對迭代輸出的While迴圈做任何修改都可以正常工作。