• Javascript设计模式之我见:迭代器模式


    大家好!本文介绍迭代器模式及其在Javascript中的应用。

    模式介绍

    定义

    提供一种方法顺序一个聚合对象中各个元素,而又不暴露该对象内部表示。

    类图及说明

    Iterator抽象迭代器

    抽象迭代器负责定义访问和遍历元素的接口,而且基本上是有固定的3个方法:first()获得第一个元素,next()访问下一个元素,isDone()(或者为hasNext())是否已经访问到底部

    ConcreIterator具体迭代器

    具体迭代器角色要实现迭代器接口,完成容器元素的遍历。

    Aggregate抽象容器

    容器角色负重提供创建具体迭代器角色的接口,必然提供一个类似createIterator()(或者为iterator())这样的方法。

    Concrete Aggregate具体容器

    具体容器实现容器接口定义的方法,创建出容纳迭代器的对象。

    应用场景

    • 访问一个聚合对象的内容而无需暴露它的内部表示。

    • 支持对聚合对象的多种遍历。 

    • 为遍历不同的聚合结构提供一个统一的接口。

    优点

    1. 支持以不同的方式遍历一个聚合,复杂的聚合可用多种方式进行遍历。
    2. 迭代器简化了聚合的接口。有了迭代器的遍历接口,聚合本身就不需要类似的遍历接口了,这样就简化了聚合的接口。
    3. 在同一个聚合上可以有多个遍历 每个迭代器保持它自己的遍历状态。因此你可以同时进行多个遍历。

    缺点 

    1. 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
    2. 抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展,例如 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中。

    参考资料

    《设计模式之禅》

    迭代器模式

  • 相关阅读:
    MCS锁——可伸缩的自旋锁
    The Art of Multiprocessor Programming读书笔记 (更新至第3章)
    在Visual Studio 2015的Cordova项目中使用Gulp
    SharePoint服务器端对象模型 之 使用LINQ进行数据访问操作(Part 1)
    SharePoint服务器端对象模型 之 使用CAML进行数据查询(Part 4)
    knockoutJs在移动设备上有时无法更新控件值
    SharePoint服务器端对象模型 之 使用CAML进行数据查询(Part 3)
    SharePoint服务器端对象模型 之 使用CAML进行数据查询(Part 2)
    SharePoint服务器端对象模型 之 使用CAML进行数据查询
    SharePoint服务器端对象模型 之 访问文件和文件夹(Part 4)
  • 原文地址:https://www.cnblogs.com/chaogex/p/3342274.html
Copyright © 2020-2023  润新知