『面试的底气』—— 设计模式之迭代器模式

1,322 阅读2分钟

定义

迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。

迭代器模式是一个非常简单的设计模式。简单到迭到很多时候我们都不认为它是一种设计模式,因为目前的绝大部分语言都内置了迭代器,比如JavaScript中的Array.prototype.forEach

实现

迭代器模式的实现非常简单,如下所示

const each = (ary, callback) => {
  for (let i = 0, l = ary.length; i < l; i++) {
    callback.call(ary[i], i, ary[i]);
  }
};
each([1, 2, 3], function (i, n) {
  console([i, n]);
});

通过each方法将ary数组每个元素传入callback中处理,这就是迭代器模式。

内部迭代器和外部迭代器

迭代器可以分为内部迭代器和外部迭代器,上述实现的each函数属于内部迭代器,each函数的内部已经定义好了迭代规则,它完全接手整个迭代过程,外部只需要一次初始调用。

那么现在有一个需求,构造一个函数来比较ary1ary2的内容是否相等。是否可以用each函数实现。当然可以了,如下这样实现:

const compare = (ary1, ary2) => {
  if (ary1.length !== ary2.length) {
    throw new Error('ary1 和 ary2 不相等');
  }
  each(ary1, function (i, n) {
    if (n !== ary2[i]) {
      throw new Error('ary1 和 ary2 不相等');
    }
  });
  console.log('ary1 和 ary2 相等');
};
compare( [ 1, 2, 3 ], [ 1, 2, 5 ] ); // 'ary1 和 ary2 不相等' ; 

但是这样实现太不优雅了,each函数中的迭代规则已经在内部定义好了,无法改变,这也是内部迭代器的缺点。

要让compare函数内部实现得优雅点,得借助外部迭代器。外部迭代器必须显式地请求迭代下一个元素。 外部迭代器增加了一些调用的复杂度,但相对也增强了迭代器的灵活性,我们可以手工控制迭代的过程或者顺序。

下面用外部迭代器来实现compare函数。

const Iterator = function (obj) {
  let current = 0;
  const next = function () {
    current += 1;
  };
  const isDone = function () {
    return current >= obj.length;
  };
  const getCurrItem = function () {
    return obj[current];
  };
  return {
    next: next,
    isDone: isDone,
    getCurrItem: getCurrItem
  }
};
const compare = function (iterator1, iterator2) {
  while (!iterator1.isDone() && !iterator2.isDone()) {
    if (iterator1.getCurrItem() !== iterator2.getCurrItem()) {
      throw new Error('iterator1 和 iterator2 不相等');
    }
    iterator1.next();
    iterator2.next();
  }
  console.log('iterator1 和 iterator2 相等');
}
const iterator1 = Iterator([1, 2, 3]);
const iterator2 = Iterator([1, 2, 3]);
compare(iterator1, iterator2); // 输出:iterator1 和 iterator2 相等 

其中 Iterator函数就是一个外部迭代器,返回一个对象,其中暴露出一个getCurrItem方法来显式地请求迭代下一个元素。

外部迭代器虽然调用方式相对复杂,但它的适用面更广,也能满足更多变的需求。

如何终止迭代

很简单,在需要终止的地方写上,return false