3-4设计模式之迭代器模式

3.4.1 模式意图:

系统中会有对集合的元素进行自增或者自减顺序的访问操作,对于这种需求我们可以使用迭代器模式来建立对应的迭代器,C#自带的IEnumerator也是利用了这种模式的特点。

file

3.4.2 模式概念:

它属于行为型模式,提供一种方法,顺序访问聚合对象中的各个元素,而又不暴露该对象的内部表示。

3.4.3 模式元素:

  • 迭代器抽象(IIterator)
  • 迭代器细节(ConcreteIterator)
  • 需要迭代的集合(Group)

3.4.4 代码示例:

下面笔者逐步完成迭代器模式的构建。
创建一个简单的集合。

    public class Group
    {
        private IList<object> items = new List<object>();
        public int Count
        {
            get { return items.Count; }
        }
        public object this[int index]
        {
            get { return items[index]; }
            set { items.Insert(index, value); }
        }
    }

为了让多种集合都能使用同一种遍历方式,我们再创建一个通用迭代器。

    public class ConcreteIterator
    {
        private IList<object> items = new List<object>();
        public Iterator(IList<object> tempItems)
        {
            items = tempItems;
        }
        private int index = -1;
        public object Current
        {
            get { return items[index]; }
        }
        public bool MoveNext()
        {
            return items.Count > ++index;
        }
        public void Reset()
        {
            index = -1;
        }
    }

然后把迭代器镶嵌到这个集合中。

    public class Group
    {
        private IList<object> items = new List<object>();
        public int Count
        {
            get { return items.Count; }
        }
        public object this[int index]
        {
            get { return items[index]; }
            set { items.Insert(index, value); }
        }

        public ConcreteIterator GetIterator()
        {
            return new ConcreteIterator(items);
        }
    }

调用示例代码。

    void Start()
    {
        Group group = new Group();
        group[0] = "爱";
        group[1] = "生";
        group[2] = "活";
        group[3] = "爱";
        group[4] = "海";
        group[5] = "澜";

        Iterator iterator = group.GetIterator();

        while (iterator.MoveNext())
        {
            this.Log($"现在的元素为:{iterator.Current}");
        }
    }

开始重构

Iterator中不能确定这个遍历的集合是列表、字典还是数组,但是可以确定他们都含有CurrentMoveNextReset三种元素,所以将这三种元素进行抽象,与原来的迭代器类进行剥离。

    public interface IIterator
    {
        object Current { get; }
        bool MoveNext();
        void Reset();
    }

原迭代器类继承接口IIterator

    public class ConcreteIterator: IIterator
    {
        private IList<object> items = new List<object>();
        public Iterator(IList<object> tempItems)
        {
            items = tempItems;
        }
        private int index = -1;
        public object Current
        {
            get { return items[index]; }
        }
        public bool MoveNext()
        {
            return items.Count > ++index;
        }
        public void Reset()
        {
            index = -1;
        }
    }

既然原迭代器类继承接口IIterator,那么可以根据这个接口所规定的结构,让细节的实现统统转移到子类中实现,也就是说,我们可以定义出多种满足不同迭代需求的迭代类,但是调用的函数都是一样的。根据里氏替换原则可知,对应的Group中所返回的public Iterator GetIterator()完全可以用接口IIterator代替。

    public class Group
    {
        private IList<object> items = new List<object>();
        public int Count
        {
            get { return items.Count; }
        }
        public object this[int index]
        {
            get { return items[index]; }
            set { items.Insert(index, value); }
        }

        public IIterator GetIterator()
        {
            return new ConcreteIterator(items);
        }
    }

最后,为了区分什么样子的集合可以使用通用迭代模式,我们做一个获得迭代器的接口,只要继承这个接口的集合,就都可以使用通用迭代模式了。

    public interface IEnumerable
    {
        ConcreteIterator GetIterator();
    }

    public class Group: IEnumerable
    {
        private IList<object> items = new List<object>();
        public int Count
        {
            get { return items.Count; }
        }
        public object this[int index]
        {
            get { return items[index]; }
            set { items.Insert(index, value); }
        }

        public IIterator GetIterator()
        {
            return new ConcreteIterator(items);
        }
    }

3.4.5 写法对比:

3.4.6 模式分析:

经过剥离和抽象,我们的迭代器模式就完成了。迭代器就是把原有需要遍历的结构进行一次包装,把包装后的结构再统一进行遍历。

其实在C# 中自带的迭代器要比我们这个更强大,它最大的特点就是按需逐步查询 Unity 之数据集合解析中的最后IEnumerable补充部分有例子说明,这里就不在熬述。

3.4.7 应用场景:

需要按指定顺序迭代查询集合中的元素。

3.4.8 小结:

如果系统自带的迭代器更好,就用系统的,这种无用的轮子我们还是不要浪费时间再造了,了解下原理就好。


更多设计模式详见:设计模式全家桶

发表评论