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中。

参考资料

《设计模式之禅》

迭代器模式

posted @ 2013-10-12 13:47  杨元超  阅读(1011)  评论(2编辑  收藏  举报