最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 精读《设计模式 - Iterator 迭代器模式》

    正文概述 掘金(黄子毅在掘金)   2021-01-25   366

    Iterator(迭代器模式)

    Iterator(迭代器模式)属于行为型模式。

    意图:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

    这种设计模式要解决的根本问题是,聚合的种类有很多,比如对象、链表、数组、甚至自定义结构,但遍历这些结构时,不同结构的遍历方式又不同,所以我们必须了解每种结构的内部定义才能遍历。

    比如数组我们可以利用 length + for 循环,对象我们可以 Object.keys,链表比较麻烦,需要内部暴露出元素的 next 以操作指向下一个元素。

    迭代器模式可以做到用同一种 API 遍历任意类型聚合对象,且不用关心聚合对象的内部结构。

    这种模式和 Array.from 有点像,但其实真正的迭代器在 JS 里是 obj[Symbol.iterator](),也就是一个对象实现了 [Symbol.interator],就认为是可遍历的。

    举例子

    如果看不懂上面的意图介绍,没有关系,设计模式需要在日常工作里用起来,结合例子可以加深你的理解,下面我准备了三个例子,让你体会什么场景下会用到这种设计模式。

    迭代器的例子非常简单,我们平时工作中有大量使用到。

    generator

    generator 天生为迭代器的 API:

    function* func () {
      yield 'a';
      yield 'b';
      return 'c';
    }
    
    var run = func();
    run.next() // {value: "a", done: false}
    run.next() // {value: "b", done: false}
    run.next() // {value: "c", done: true}
    

    我们无需关心 generator 内部是何种存储结构,只需要调用 .next(),并根据返回的 done 来判断是否遍历完即可。在 generator 的场景中,迭代器不仅用来遍历聚合,还用于执行代码。

    数组迭代器

    我们可以用迭代器的方式遍历数组:

    const arr = [1, 2, 3]
    const run = arr[Symbol.iterator]()
    
    run.next() // {value: 1, done: false}
    run.next() // {value: 2, done: false}
    run.next() // {value: 2, done: false}
    run.next() // {value: undefined, done: true}
    

    可能有人觉得这是画蛇添足,因为毕竟遍历数组用 for 循环更方便,但这就是设计模式与非设计模式思维的区别,重要的不是用熟悉简单的 API 快速满足需求,设计模式关注的是如何统一、抽象、低耦合的编码

    Map 迭代器

    Map 对象也可以用迭代器方式遍历:

    const citys = new Map([['北京', 1], ['上海', 2], ['杭州', 3]])
    const run = citys.entries()
    
    run.next() // {value: ['北京', 1], done: false}
    run.next() // {value: ['上海', 2], done: false}
    run.next() // {value: ['杭州', 3], done: false}
    run.next() // {value: undefined, done: true}
    

    意图解释

    从上面的例子可以看出,虽然用迭代器遍历数组看上去比 for 循环麻烦一点,但当我们把所有聚合类型放到一起看时,可以发现只有迭代器的 API 是最统一的,是唯一一个不需要关心聚合类型就可以完成遍历的方案。

    意图:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

    再来看意图,就非常好理解了,我们无需关心 数组、generator、Map 内部是如何存储的,就可以进行遍历。实际上,深究 generator 内部的存储结构也没有意义,如果我们不用迭代器进行遍历,那么对于复杂结构的遍历成本是非常高的。

    结构图

    精读《设计模式 - Iterator 迭代器模式》
    • Aggregate: 聚合,需要定义创建迭代器的接口。比如前端规范的 [Symbol.iterator](),或者这里定义的 CreateIterator()
    • Iterator: 迭代器,定义了访问与遍历的 API。

    迭代器的定义很简单,实现时要考虑的因素可不少,包括:

    • 健壮性。即迭代过程中增加、删除元素后,还能正常遍历。或者遍历空聚合时也要能正常工作。
    • 外部控制迭代还是内部。即类似 KOA 由插件调用 next() 控制迭代,还是由外层统一控制迭代。
    • 如何定义遍历算法。即便对于对象这种简单场景,也存在深度优先和广度优先、冒泡与捕获这几种遍历顺序,迭代器可以提供选择或者拓展的方式,自定义遍历算法。

    代码例子

    下面例子使用 typescript 编写。

    // 定义聚合接口
    interface Aggregate{
      getIterator: () => Iterator
    }
    
    // 定义迭代器接口
    interface Iterator {
      // 指向下一个
      next: () => void
    }
    
    // 定义一个聚合
    class List implements Aggregate {
      // 存储元素
      public values: string[]
    
      // 游标
      public index: number
    
      getIterator() {
        return new ConcreteIterator(this);
      }
    }
    
    // List 的迭代器
    class ConcreteIterator implements Iterator {
      constructor(list: List) {
        this.list = list
      }
    
      next() {
        return this.list.values[this.list.index] // 注意边界情况,这里就不展开
        this.list.index++
      }
    }
    

    弊端

    如果你只是遍历数组,直接用 for 循环会比迭代器方便很多,没必要为了用设计模式而用设计模式。迭代器仅在以下情况可以考虑用于数组:

    1. 这个数组比较特殊,是 N 维数组,需要一次性遍历完,那么可以用迭代器。
    2. 同时遍历数组和其他类型的聚合,则不论数组还是其他聚合,都用相同的迭代器模式遍历最好。

    总结

    迭代器模式比较好理解,这里补充几个相关设计模式:

    • 迭代器可以和组合模式配合,在组合结构内进行递归,这样一个迭代器就可以遍历完所有组合。
    • 可以用工厂模式 + 多态模式,实例化不同的迭代器的实例。
    • 迭代器模式还可以与备忘录模式配合使用,当我们要还原迭代器状态时,适合在迭代器内部使用备忘录模式进行状态存储。

    如果你想参与讨论,请 点击这里,每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。


    下载网 » 精读《设计模式 - Iterator 迭代器模式》

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元