行为型-迭代器模式

概念

Iterator Pattern:别名为游标(Cursor),提供一个方法来访问聚合对象,而不用暴露这个对象的内部表示。

img

使用

  • 内部迭代器:内部定义好了迭代规则,完全接手了整个过程,外部只需要调用一次。但是它只能迭代一个数组,要想迭代两个数组必须改变内部函数的实现,违反了开闭原则
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 实现一个each函数,第一个参数是数组,第二个参数是每一步的处理函数
let each = function (arr, callback) {
for(let i = 0,l = arr.length; i < l;i++) {
callback.call(arr[i], i, arr[i]);
}
}

// test
each([1, 2, 3], function(i, n) {
console.log([i, n])
})
// [0, 1]
// [1, 2]
// [2, 3]
  • 外部迭代器:必须显式的请求迭代下一个元素,增加了一些调用的复杂度,但相对也增强了迭代器的灵活性,因此我们可以手动地控制整个迭代过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var Iterator = function (obj) {
var current = 0;

var next = function () {
current += 1;
};

var isDone = function () {
return current >= obj.length;
};

var getCurrItem = function () {
return obj[current];
};

return {
next: next,
isDone: isDone,
getCurrItem: getCurrItem,
length: obj.length
};
}

从上面的迭代器实现可以看出,只要被迭代的聚合对象拥有length属性而且可以用下标访问,那么它就是迭代器。Javascript中的类数组对象也是迭代器。

优点

  • 支持以不同的方式遍历一个聚合对象,同一个聚合对象可以定义多种遍历方式
  • 简化了聚合类:在原有的聚合对象中不需要再自行提供数据遍历等方法
  • 增加新的聚合类和迭代器类都很方便,无须修改原有代码,符合开闭原则

缺点

  • 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,一定程度上增加了系统的复杂性
  • 抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展,例如JDK内置迭代器Iterator就无法实现逆向遍历,如果需要实现逆向遍历,只能通过其子类ListIterator等来实现,而ListIterator迭代器无法用于操作Set类型的聚合对象。在自定义迭代器时,创建一个考虑全面的抽象迭代器并不是件很容易的事情