跳到主要内容

17、C#设计模式 - 迭代器模式

迭代器模式(Iterator Pattern)

迭代器模式属于行为型模式,它提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。

角色:

1、 迭代器(Iterator);

迭代器角色负责定义访问和遍历元素的接口;

2、 具体迭代器(ConcreteIteraror);

具体迭代器角色实现了迭代器接口,并需要记录遍历中的当前位置;

3、 聚合(Aggregate);

聚合角色负责定义获得迭代器角色的接口;

4、 具体聚合(ConcreteAggregate);

具体聚合角色实现聚合角色接口。

示例:

 

命名空间IteratorPattern中包含Person基类、People类、PelpleEnum类,另外包含一个苹果手机ApplePhone类,和BestEnum类。Person示例来自微软官方的IEnumerable接口介绍页面,BestEnum则使用.Net 2.0中的yield return关键字创建苹果手机信息序列。

namespace IteratorPattern

一、传统迭代器实现

public class Person {

    public string FirstName { get; set; }
    public string LastName { get; set; }

    public Person(string firstName, string lastName) {
        this.FirstName = firstName;
        this.LastName = lastName;
    }

}

Person类,包含First Name和Last Name。

public class People : IEnumerable {

    private Person[] _people = null;

    public People(Person[] pArray) {
        _people = new Person[pArray.Length];

        for (int i = 0; i < pArray.Length; i++) {
            _people[i] = pArray[i];
        }
    }

    public IEnumerator GetEnumerator() {
        return new PeopleEnum(_people);
    }

}

People类,实现IEnumerable接口。

public class PeopleEnum : IEnumerator {

    private Person[] _people = null;

    private int _cursor = -1;

    public PeopleEnum(Person[] list) {
        _people = list;
    }

    public bool MoveNext() {
        _cursor++;
        return (_cursor < _people.Length);
    }

    public void Reset() {
        _cursor = -1;
    }

    public object Current {
        get {
            try {
                return _people[_cursor];
            }
            catch (IndexOutOfRangeException) {
                throw new InvalidOperationException();
            }
        }
    }

}

PeopleEnum类,实现IEnumerator接口。

二、yield return迭代器实现

yield关键字向编译器指示它所在的方法是迭代器块,yield return返回一个迭代器的状态机。

public class ApplePhone {

    public string PhoneName { get; set; }

    public DateTime PublishedDate { get; set; }

}

ApplePhone类,包含手机名称和发布日期。

public class BestEnum {

    public static IEnumerable<ApplePhone> GetIPhones() {
        yield return new ApplePhone {
            PhoneName = "IPhone",
            PublishedDate = new DateTime(2007, 1, 9)
        };
        yield return new ApplePhone {
            PhoneName = "IPhone 3G",
            PublishedDate = new DateTime(2008, 6, 10)
        };
        yield return new ApplePhone {
            PhoneName = "IPhone 3GS",
            PublishedDate = new DateTime(2009, 6, 9)
        };
        yield return new ApplePhone {
            PhoneName = "IPhone 4",
            PublishedDate = new DateTime(2010, 6, 8)
        };
        //部分代码已省略
    }

}

BestEnum类,包含GetIPhones方法返回苹果手机信息的序列。

public class Program {

    protected const string LINE_BREAK =
        "---------------------------------------------";

    public static void Main(string[] args) {
        var peopleArray = new Person[]
        {
            new Person("John", "Smith"),
            new Person("Jim", "Johnson"),
            new Person("Sue", "Rabon")
        };

        var peopleList = new People(peopleArray);
        foreach(Person p in peopleList)
            Console.WriteLine(p.FirstName + " " + p.LastName);

        Console.WriteLine(LINE_BREAK);

        var iterator = peopleList.GetEnumerator();
        while(iterator.MoveNext()) {
            var person = iterator.Current as Person;
            Console.WriteLine(person.FirstName + " " + person.LastName);
        }

        Console.WriteLine(LINE_BREAK);

        foreach(var phone in BestEnum.GetIPhones()) {
            Console.WriteLine("[" + phone.PhoneName + "] was released in " +
                              phone.PublishedDate.ToString("yyyy-MM-dd") + "!");
        }

        Console.WriteLine(LINE_BREAK);

        Console.ReadKey();
    }

}

以上是调用方的代码,以下是这个案例的输出结果:

John Smith
Jim Johnson
Sue Rabon
---------------------------------------------
John Smith
Jim Johnson
Sue Rabon
---------------------------------------------
[IPhone] was released in 2007-01-09!
[IPhone 3G] was released in 2008-06-10!
[IPhone 3GS] was released in 2009-06-09!
[IPhone 4] was released in 2010-06-08!
[IPhone 4s] was released in 2011-10-04!
[IPhone 5] was released in 2012-09-13!
[IPhone 5S] was released in 2013-09-10!
[IPhone 5C] was released in 2013-09-10!
[IPhone 6] was released in 2014-09-10!
[IPhone 6 Plus] was released in 2014-09-10!
[IPhone 6s] was released in 2015-09-10!
[IPhone 6s Plus] was released in 2015-09-10!
[IPhone 7] was released in 2016-09-08!
[IPhone 7 Plus] was released in 2016-09-08!
[IPhone 8] was released in 2017-09-13!
[IPhone 8 Plus] was released in 2017-09-13!
[IPhone X] was released in 2017-09-13!
---------------------------------------------

优点:

1、 迭代器模式使得访问一个聚合对象的内容而无需暴露它的内部表示,即迭代抽象;
2、 迭代器模式为遍历不同的集合结构提供了一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作;

缺点:

1、 迭代器模式在遍历的同时更改迭代器所在的集合结构会导致出现异常所以使用foreach语句只能在对集合进行遍历,不能在遍历的同时更改集合中的元素;

使用场景:

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