zoukankan      html  css  js  c++  java
  • js迭代器模式

    迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。

    • 一个聚集对象,而且不管这些对象是什么都需要遍历的时候,你就应该考虑用迭代器模式。
    • 你需要对聚集有多种方式遍历时,可以考虑用迭代器模式。为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪一项等统一的接口。
    • 迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。

    既然,迭代器模式承担了遍历集合对象的职责,则该模式自然存在2个类,一个是聚合类,一个是迭代器类。在面向对象涉及原则中还有一条是针对接口编程,所 以,在迭代器模式中,抽象了2个接口,一个是聚合接口,另一个是迭代器接口,这样迭代器模式中就四个角色了,具体的类图如下所示:

    从上图可以看出,迭代器模式由以下角色组成:

    • 迭代器角色(Iterator):迭代器角色负责定义访问和遍历元素的接口
    • 具体迭代器角色(Concrete Iteraror):具体迭代器角色实现了迭代器接口,并需要记录遍历中的当前位置。
    • 聚合角色(Aggregate):聚合角色负责定义获得迭代器角色的接口
    • 具体聚合角色(Concrete Aggregate):具体聚合角色实现聚合角色接口。

    在下面的情况下可以考虑使用迭代器模式:

    • 系统需要访问一个聚合对象的内容而无需暴露它的内部表示。
    • 系统需要支持对聚合对象的多种遍历。
    • 系统需要为不同的聚合结构提供一个统一的接口。

    C#迭代器模式:

    namespace 迭代器模式
    {
        class Program
        {
            static void Main(string[] args)
            {
                ConcreteAggregate a = new ConcreteAggregate();
    
                a[0] = "大鸟";
                a[1] = "小菜";
                a[2] = "行李";
                a[3] = "老外";
                a[4] = "公交内部员工";
                a[5] = "小偷";
    
                Iterator i = new ConcreteIterator(a);
                //Iterator i = new ConcreteIteratorDesc(a);
                object item = i.First();
                while (!i.IsDone())
                {
                    Console.WriteLine("{0} 请买车票!", i.CurrentItem());
                    i.Next();
                }
    
                Console.Read();
            }
        }
    
        abstract class Aggregate
        {
            public abstract Iterator CreateIterator();
        }
    
        class ConcreteAggregate : Aggregate
        {
            private IList<object> items = new List<object>();
            public override Iterator CreateIterator()
            {
                return new ConcreteIterator(this);
            }
    
            public int Count
            {
                get { return items.Count; }
            }
    
            public object this[int index]
            {
                get { return items[index]; }
                set { items.Insert(index, value); }
            }
        }
    
        abstract class Iterator
        {
            public abstract object First();
            public abstract object Next();
            public abstract bool IsDone();
            public abstract object CurrentItem();
        }
    
        class ConcreteIterator : Iterator
        {
            private ConcreteAggregate aggregate;
            private int current = 0;
    
            public ConcreteIterator(ConcreteAggregate aggregate)
            {
                this.aggregate = aggregate;
            }
    
            public override object First()
            {
                return aggregate[0];
            }
    
            public override object Next()
            {
                object ret = null;
                current++;
    
                if (current < aggregate.Count)
                {
                    ret = aggregate[current];
                }
    
                return ret;
            }
    
            public override object CurrentItem()
            {
                return aggregate[current];
            }
    
            public override bool IsDone()
            {
                return current >= aggregate.Count ? true : false;
            }
    
        }
    
        class ConcreteIteratorDesc : Iterator
        {
            private ConcreteAggregate aggregate;
            private int current = 0;
    
            public ConcreteIteratorDesc(ConcreteAggregate aggregate)
            {
                this.aggregate = aggregate;
                current = aggregate.Count - 1;
            }
    
            public override object First()
            {
                return aggregate[aggregate.Count - 1];
            }
    
            public override object Next()
            {
                object ret = null;
                current--;
                if (current >= 0)
                {
                    ret = aggregate[current];
                }
    
                return ret;
            }
    
            public override object CurrentItem()
            {
                return aggregate[current];
            }
    
            public override bool IsDone()
            {
                return current < 0 ? true : false;
            }
    
        }
    }

    C#.NET的迭代器实现:

    namespace NET的迭代器实现
    {
        class Program
        {
            static void Main(string[] args)
            {
    
                IList<string> a = new List<string>();
                a.Add("大鸟");
                a.Add("小菜");
                a.Add("行李");
                a.Add("老外");
                a.Add("公交内部员工");
                a.Add("小偷");
    
                foreach (string item in a)
                {
                    Console.WriteLine("{0} 请买车票!", item);
                }
    
                IEnumerator<string> e = a.GetEnumerator();
                while (e.MoveNext())
                {
                    Console.WriteLine("{0} 请买车票!", e.Current);
    
                }
                Console.Read();
            }
        }
    }

    js内部迭代器:

    现在我们来实现一个each函数,each函数接受2个参数,第一个为被循环的数组,第二个为循环中的每一步后将被触发的回调函数:

    var each = function(ary,callback){
        for(var i=0,l=ary.length;i<l;i++){
            callback.call(ary[i],i,ary[i]);  //把下标和元素当作参数传递给callback函数
        }
    };
    
    each([1,2,3],function(i,n){
        alert([i,n]);
    });

    js外部迭代器:

    比如现在有个需求,要判断2个数组里的元素的值是否完全相等,先看一下不使用外部迭代器的写法:

    var compare = function(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不相等');
            }
        });
        alert('ary1和ary2相等');
    };
    
    compare([1,2,3],[1,2,4]);   //throw new Error('ary1和ary2不相等');

    说实话,这个compare函数一点都算不上好看,我们目前能够顺利的完成需求,还要感谢在javascript里可以把函数当作参数传递的特性,但在其他的语言中未必就能如此幸运。
    在一些没有闭包的语言中,内部迭代器本身的实现也相当复杂,比如C语言中的内部迭代器是用函数指针来实现的,循环处理需要的数据都要以参数的形式明确地从外面传递进去。

    外部迭代器必须显式地请求迭代下一个元素。
    外部迭代器增加了一些调用的复杂度,但相对也增强了迭代器的灵活性,我们可以手工控制迭代的过程或者顺序。
    下面这个外部迭代器的实现来自《松本行弘的程序世界》第4章,原例用Ruby写成,这里我们翻译成javascript:

    var Iterator = function(obj){
        var current = 0;
    
        var next = function(){
            current += 1;
        };
    
        var isDone = function(){
            return current >= obj.length;
        };
    
        var getCurrItem = function(){
            return obj[current];
        };
    
        return{
            next:next,
            isDone:isDone,
            getCurrItem:getCurrItem
        }
    };
    
    再看看如何改写compare函数:
    
    var compare = function(iterator1,iterator2){
        while(!iterator1.isDone() && !iterator2.isDone()){
            if(iterator1.getCurrItem() !== iterator2.getCurrItem()){
                throw new Error('iterator1和iterator2不相等');
            }
            iterator1.next();
            iterator2.next();
        }
        alert('iterator1和iterator2相等');
    };
    
    var iterator1 = Iterator([1,2,3]);
    var iterator2 = Iterator([1,2,3]);
    
    compare(iterator1,iterator2);    //输出:iterator1和iterator2相等

    外部迭代器虽然调用方式相对复杂,但它的适用面更广,也能满足更多变的需求。内部迭代器和外部迭代器在实际生产中没有优劣之分,究竟使用哪个要根据需求场景而定。

    迭代类数组对象和字面量对象:

    var isArraylike = function(obj){
        return Object.prototype.toString.call(obj) === '[object Array]';
    };
    
    var each = function(obj,callback){
        var value,
            i = 0,
            length = obj.length,
            isArray = isArraylike(obj);
           
        if(isArray){ //迭代类数组
            for(;i<length;i++){
                value = callback.call(obj[i],i,obj[i]);
                if(value === false){
                    break;
                }
            }
        }else{
            for(i in obj){
                value = callback.call(obj[i],i,obj[i]);       
                if(value === false){
                    break;
                }
            }
        }
    };
    
    each([1,2,3,4,5],function(i,n){
        if(n>3){
            return false;
        }
        alert(n); //分别输出:1,2,3
    });
    
    each({a:1,b:2,c:3,d:4,e:5},function(i,n){
        if(n>3){
            return false;
        }
        alert(n); //分别输出:1,2,3
    });

    js倒序迭代器:

    var reverseEach = function(ary,callback){
        for(var l=ary.length-1;l>=0;l--){
            callback.call(ary[l],l,ary[l]);
        }
    };
    
    reverseEach([0,1,2],function(i,n){
        alert(n); //分别输出:2,1,0
    });

    js中止迭代器:

    var each = function(ary,callback){
        for(var i=0,l=ary.length;i<l;i++){
            if(callback.call(ary[i],i,ary[i]) === false){   //callback的执行结果返回false,提前终止迭代
                break;
            }
        }
    };
    
    each([1,2,3,4,5],function(i,n){
        if(n>3){
            return false;
        }
        alert(n); //分别输出:1,2,3
    });
  • 相关阅读:
    windows下python访问ipv6报错
    windows下python的包管理器pip安装
    python添加windows域验证
    Java系列笔记(1)
    JVM调优总结 -Xms -Xmx -Xmn -Xss
    5种调优Java NIO和NIO.2的方式
    Java之美[从菜鸟到高手演变]之JVM内存管理及垃圾回收
    jmap,jhat分析内存
    JVM 垃圾回收算法
    JVM 类加载过程
  • 原文地址:https://www.cnblogs.com/gongshunkai/p/6627201.html
Copyright © 2011-2022 走看看