Javascript設計模式之我見:迭代器模式
大家好!本文介紹迭代器模式及其在Javascript中的應用。
模式介紹
定義
提供一種方法順序一個聚合對象中各個元素,而又不暴露該對象內部表示。
類圖及說明

Iterator抽象迭代器
抽象迭代器負責定義訪問和遍歷元素的接口,而且基本上是有固定的3個方法:first()獲得第一個元素,next()訪問下一個元素,isDone()(或者為hasNext())是否已經訪問到底部
ConcreIterator具體迭代器
具體迭代器角色要實現迭代器接口,完成容器元素的遍歷。
Aggregate抽象容器
容器角色負重提供創建具體迭代器角色的接口,必然提供一個類似createIterator()(或者為iterator())這樣的方法。
Concrete Aggregate具體容器
具體容器實現容器接口定義的方法,創建出容納迭代器的對象。
應用場景
-
訪問一個聚合對象的內容而無需暴露它的內部表示。
-
支持對聚合對象的多種遍歷。
-
為遍歷不同的聚合結構提供一個統一的接口。
優點
- 支持以不同的方式遍歷一個聚合,復雜的聚合可用多種方式進行遍歷。
- 迭代器簡化了聚合的接口。有了迭代器的遍歷接口,聚合本身就不需要類似的遍歷接口了,這樣就簡化了聚合的接口。
- 在同一個聚合上可以有多個遍歷 每個迭代器保持它自己的遍歷狀態。因此你可以同時進行多個遍歷。
缺點
- 由于迭代器模式將存儲數據和遍歷數據的職責分離,增加新的聚合類需要對應增加新的迭代器類,類的個數成對增加,這在一定程度上增加了系統的復雜性。
- 抽象迭代器的設計難度較大,需要充分考慮到系統將來的擴展,例如 JDK 內置迭代器 Iterator 就無法實現逆向遍歷,如果需要實現逆向遍歷,只能通過其子類ListIterator 等來實現,而 ListIterator 迭代器無法用于操作 Set 類型的聚合對象。在自定義迭代器時,創建一個考慮全面的抽象迭代器并不是件很容易的事情。
迭代器模式在Javascript中的應用
我的理解
抽象迭代器角色
定義對數據結構的通用基本操作。如hasNext、next等。
具體迭代器角色
實現對某一類數據結構的基本操作。如ArrIterator實現對數組結構的基本操作,hashIterator實現對hash結構的基本操作
容器角色
實現數據結構的特定操作。
類圖及說明
介紹兩種形式的應用:
繼承

優點
- 容器類可直接復用迭代器的操作,不用再提供方法iterator來獲得迭代器實例了。
缺點
- 容器類繼承了迭代器的所有操作,有些操作它可能不會用到。
- 限定了迭代器的擴展,在修改迭代器時可能會影響到容器類。
適用場合
- 迭代器比較簡單
- 容器類需要使用所有的迭代器方法
委托

優點
- 容器類可以只使用迭代器的部分操作。
- 靈活,便于容器類與迭代器類擴展。
缺點
- 容器類中需要增加委托方法iterator。
適用場合
- 迭代器類和容器類需要擴展
示例
大神可以拿各種offer,屌絲表示很是好奇。一天屌絲偷偷搞到了大神讀的書籍清單,于是迫不及待地打開,想看個究竟。
類圖

代碼
代碼中使用的庫:YOOP
IBook
var IBook = YYC.Interface("showInfo");
Book
var Book = YYC.Class({Interface: IBook}, { Init: function (name) { this._name = name; }, Private: { _name: "" }, Public: { showInfo: function () { console.log(this._name); } } });
場景類
function main(){ //定義一個數組容器,存放所有的書對象 var container, i, len; container = []; i = 0; len = 0; container.push(new Book("設計模式之禪")); container.push(new Book("Http權威指南")); container.push(new Book("深入理解計算機操作系統")); for(i = 0, len = container.length; i < len; i++){ container[i].showInfo(); } }
運行結果

示例分析
場景類中實現了一個數組容器及其遍歷。應該把容器的實現封裝起來形成容器類,令場景類調用容器的接口方法。
另外,容器類中訪問數組容器元素的邏輯具有通用性,可以提出來形成迭代器類。凡是需要訪問數組容器元素的容器類,只要使用迭代器類就可以實現。
使用迭代器模式
現在分別用繼承和委托的方式來實現。
繼承
可以將內部容器container放到Iterator類中。
類圖

代碼
IIterator
var IIterator = YYC.Interface("hasNext", "next");
Iterator
var Iterator = YYC.Class({Interface: IIterator}, { Init: function () { }, Private: { _container: [], _cursor: 0 }, Public: { hasNext: function () { if (this._cursor === this._container.length) { return false; } else { return true; } }, next: function () { var result = null; if (this.hasNext()) { result = this._container[this._cursor]; this._cursor += 1; } else { result = null; } return result; } } });
BookContainer
var BookContainer = YYC.Class(Iterator, { Init: function(){}, Public: { add: function(name){ this._container.push(new Book(name)); }, showInfo: function(){ while(this.hasNext()){ this.next().showInfo(); } } } });
IBook
var IBook = YYC.Interface("showInfo");
Book
var Book = YYC.Class({Interface: IBook}, { Init: function (name) { this._name = name; }, Private: { _name: "" }, Public: { showInfo: function () { console.log(this._name); } } });
場景類Client
function main() { var container = new BookContainer(); container.add("設計模式之禪"); container.add("Http權威指南"); container.add("深入理解計算機操作系統"); container.showInfo(); }
委托
Iterator中通過注入獲得內部容器container。
類圖

代碼
IIterator
var IIterator = YYC.Interface("hasNext", "next");
Iterator
var Iterator = YYC.Class({Interface: IIterator}, { Init: function (container) { this._container = container; }, Private: { _container: [], _cursor: 0 }, Public: { hasNext: function () { if (this._cursor === this._container.length) { return false; } else { return true; } }, next: function () { var result = null; if (this.hasNext()) { result = this._container[this._cursor]; this._cursor += 1; } else { result = null; } return result; } } });
IBookContainer
var IBookContainer = YYC.Interface("add", "iterator");
BookContainer
var BookContainer = YYC.Class({ Interface: IBookContainer }, { Init: function () { }, Private: { _container: [] }, Public: { add: function (name) { this._container.push(new Book(name)); }, iterator: function () { return new Iterator(this._container); } } });
IBook
var IBook = YYC.Interface("showInfo");
Book
var Book = YYC.Class({Interface: IBook}, { Init: function (name) { this._name = name; }, Private: { _name: "" }, Public: { showInfo: function () { console.log(this._name); } } });
場景類Client
function main() { var container, iter; container = new BookContainer(); container.add("設計模式之禪"); container.add("Http權威指南"); container.add("深入理解計算機操作系統"); iter = container.iterator(); while (iter.hasNext()) { iter.next().showInfo(); } }
變形
上面將容器類BookContainer的showInfo方法放到場景類中實現。也可以將其放到BookContainer中,這樣BookContainer就不需要iterator方法了。
IBookContainer
var IBookContainer = YYC.Interface("add", "showInfo");
BookContainer
var BookContainer = YYC.Class({ Interface: IBookContainer }, { Init: function () { this._iter = new Iterator(this._container); }, Private: { _container: [], _iter: null }, Public: { add: function (name) { this._container.push(new Book(name)); }, showInfo: function () { while (this._iter.hasNext()) { this._iter.next().showInfo(); } } } });
場景類
function main() { var container = new BookContainer(); container.add("設計模式之禪"); container.add("Http權威指南"); container.add("深入理解計算機操作系統"); container.showInfo(); }
運行結果

示例分析
為什么add放到容器類BookContainer,而不是放到迭代器Iterator中呢?
add: function (name) { this._container.push(new Book(name)); },
因為在add方法中需要創建Book實例,因此與容器元素Book強耦合。而Iterator中都是容器的基本操作,是不需要知道具體的容器元素的。所以add不能放到Iterator中。
又因為add屬于容器操作,因此應該放到作為容器類的BookContainer中。
參考資料
浙公網安備 33010602011771號