Hypocrite Nush
  1. 1 Hypocrite Nush
  2. 2 かかってこいよ NakamuraEmi
  3. 3 Quiet Storm Lyn
  4. 4 Last Surprise Lyn
  5. 5 Warcry mpi
  6. 6 Flower Of Life 发热巫女
  7. 7 Time Bomb Veela
  8. 8 One Last You Jen Bird
  9. 9 Libertus Chen-U
  10. 10 Life Will Change Lyn
  11. 11 The Night We Stood Lyn
2017-05-08 22:46:09

设计模式——迭代器模式

迭代器模式是一种很常见的模式,大部分语言都有了内置的迭代器实现,JS也不例外,从Array.prototype.forEach到ES6新增的专用迭代器都属于迭代器模式。下面我们来探究一下迭代器模式的封装方法。


迭代器模式封装原则

和其他设计模式一样,迭代器模式也是将变化的部分与不变的部分隔离开来。特点如下:

  1. 将遍历访问集合结构内容的逻辑封装,不暴露其内部结构。
  2. 为同一种集合结构提供相同的接口,从而支持内容不同的集合结构的遍历。

内部迭代器

内部迭代器如其名,它已经在内部完全定义好了迭代规则,只留出一个初始调用的接口。优点就是调用很方便,不必关心内部实现。缺点也是由于封装性太强导致拓展性不足。一个简单的内部迭代器实现如下:

let each = function(ary, callback) {
  for (let i = 0, l = ary.length; i < 1; i++) {
    callback.call(ary[i], i, arr[i]);
  };
}

外部迭代器

外部迭代器相比与内部迭代器拓展性更强一些,将请求下一个元素的接口暴露了出来,我们可以手动控制迭代的过程或顺序。实例如下:

let Iterator = function(obj) {
  let current = 0;
  let next = () => {
    current += 1;
  };
  let isDone = () => current >= obj.length;
  let getCurrItem = () => obj[current];
  return {
    next,
    isDone,
    getCurrItem
  };
};
let example = Iterator([1, 2, 3]);
while (!iterator.isDone()) {
  console.log(example.getCurrItem()); // 1, 2, 3
  iterator.next();
}

外部迭代器虽然调用方式复杂,但由于更灵活,也能满足更复杂的需求。具体业务中使用哪一种迭代器由业务复杂度决定。当然由于ES6的迭代器模块的出现,使得外部迭代器的创建更加简单。例子如下:

function* iterator(obj) {
  for (let current = 0; current < obj.length; current++) {
    yield obj[current];
  }
}
let example = iterator([1, 2, 3]);

更详细的内容可以去迭代器相关文章查看。

应用场景实例

我们现在要实现一个根据不同浏览器使用不同方式上传文件的需求。首先我们会优先使用控件上传,如果浏览器不存在控件则使用Flash,如果没有Flash则使用表单上传。按照逻辑很容易写出如下代码:

let getUploadObj = () => {
  try {
    return new ActiveXObject('TXFTNActiveX.FYNUpload'); // IE 控件
  } catch(e) {
    if (supportFlash()) {
      let str = "<object type='application/x-shockwave-flash'></object>";
      return $(str).appendTo($('body'));
    } else {
      let str = '<input name="file" type="file" />'; // 表单
      return $(str).appendTo($('body')); 
    }
  }
};

虽然实现了功能,但问题还是一如既往。所有的上传方式代码耦合到了一起,不仅难以阅读,而且在今后需要改进上传上式选择上也要深入内部实现。下面我们将每种方法都封装到各自的函数里,然后通过迭代器模式获取可用的方法 。

let getActiveUploadObj = () => {
  try {
    return new ActiveXObject('TXFTNActiveX.FTNUpload');
  }    catch(e) {
    return false;
  }
};
let getFlashUploadObj = () => {
  if (supportFlash()) {
     let str = "<object type='application/x-shockwave-flash'></object>";
     return $(str).appendTo($('body'));
  }
  return false;
};
let getFormUploadObj = () => {
   let str = '<input name="file" type="file" />'; // 表单
   return $(str).appendTo($('body')); 
}

现在我们已经将方法函数分别进行了封装,接下来只需要创建一个迭代器对方法函数进行遍历即可。

let iterator = (...arg) => {
  for (let i = 0, fn; fn = arg[i++];) {
    let uploadObj = fn();
    if (uploadObj !== false) {
      return uploadObj;
    }
  }
};
let uploadObj = iterator(getActiveUploadObj, getFlashUploadObj, getFormUploadObj);

重构之后,我们成功分离了方法和迭代逻辑,使得可拓展性更强。以后如果要增加其他上传方式只要新增方法函数再迭代即可。

-- EOF --

添加在分类「 前端开发 」下,并被添加 「设计模式」 标签。

文章目录

 - [迭代器模式封装原则](#迭代器模式封装原则)
 - [内部迭代器](#内部迭代器)
 - [外部迭代器](#外部迭代器)
 - [应用场景实例](#应用场景实例)
回到首页