深入浅出设计模式读书笔记—Iterator

一、模式之前:

在谈iterator之前,先看一个OO的基本原则:单一职责原则(Single responsibility principle) 。

低耦合,高内聚(Low coupling,High cohesion)是软件工程强调的一个基本指导方针,也是为广大设计人员所追求的。而单一职责原则正是用于应对此问题的。单一职责强调每个类应只负责一项功能,以应对我们的老朋友—变化(Vary)。职责(功能)常常是变化的根源,一旦一个类涉及过多的职责(功能),这个类的‘把柄’也就相应增多了。

其实,这个原则也可以为我们在写方法时提供规范。很多良好编程习惯都建议一个方法不宜过长,一个方法应只做一件事,亦即最好做到‘原子化’。 

我们经常会遇到一个类包含数据集合的情形,一般我们并不会担心类使用者如何遍历集合(遍历集合的职责),这完全得益于framework帮我们实现了一套iterator,iterator帮我们完成了遍历集合的职责。

二、模式之始:

  迭代器模式和上面描述的情形又有点小差别,在前种情形下,集合一般实现为list等,使用方可以通过foreach来遍历元素。而迭代器模式会屏蔽集合的实现,即使用者不用知道集合的是list或者array等等。

标准的iterator模式大致如下图: 

Iterator接口承担了遍历数据集合的职责(图中略去了具体的iterator)。HasNext判断是否含有更多元素,Next取出下一个元素,一般在使用时都有如下情形:

....
while( iter.HasNext() )
{
    
object item = iter.Next();
    ....
}

 

  但如前面所说,framework已经包含了一套完整的机制供我们实现iterator模式:IEnumerable和IEnumerator以及其对应的泛型接口。

首先看看IEnumerable的代码:

public interface IEnumerable
{
    IEnumerator GetEnumerator();
}

 

  很简单(类似于AbstractClass),再看下IEnumerator的实现:

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

 

  这个接口包含了与上面Iterator接口功能相似的方法。因此在C#中实现迭代器,大可不必舍近求远:

        using System;

using System.Collections;

public class Person
{
    
public Person(string fName, string lName)
    {
        
this.firstName = fName;
        
this.lastName = lName;
    }

    
public string firstName;
    
public string lastName;
}

public class People : IEnumerable
{
    
private Person[] _people;
    
public People(Person[] pArray)
    {
        _people 
= new Person[pArray.Length];

        
for (int i = 0; i < pArray.Length; i++)
        {
            _people[i] 
= pArray[i];
        }
    }
        
//实现IEnumerable
        public IEnumerator GetEnumerator()
        {
            
return new PeopleEnum(_people);
        }
}
//将遍历的职责放入PeopleEnum
public class PeopleEnum : IEnumerator
{
    
public Person[] _people;

    
// Enumerators are positioned before the first element
    
// until the first MoveNext() call.
    int position = -1;

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

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

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

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

 

  当我们需要访问People中的每一个Person时可以这样:

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

            var peopleList 
= new People(peopleArray);
            var iter 
= peopleList.GetEnumerator();
            
while (iter.MoveNext())
            {
                var person 
= iter.Current as Person;
                
if (person != null)
                {
                    Console.WriteLine(person.firstName 
+ " " + person.lastName);
                }
            }

        }

 

  让人欣喜的是我们不用每次都while(...MoveNext()){...},在.Net下只要实现了IEnumerable(或其泛型版本),就可以使用foreach来进行遍历操作,这样一来,世界变得很干净了(使用泛型版本会更干净):

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

            var peopleList 
= new People(peopleArray);
            
foreach (var obj in peopleList)
            {
                var person 
= obj as Person;
                
if( obj != null)
                {
                    Console.WriteLine(person.firstName 
+ " " + person.lastName);
                }
            }

        }

   上面的例子中出于说明模式的通用使用情形的目的,自定义了一个Enumerator,事实上,在framework中,大部分集合结构都已经实现了IEnumerble这个接口,因此很多时候我们是不用自己去实现这个接口的,而仅需要像下面这样:

  public class People : IEnumerable
    {
        
private object[] _people;
        
public People(Person[] pArray)
        {
            _people 
= new Person[pArray.Length];

            
for (int i = 0; i < pArray.Length; i++)
            {
                _people[i] 
= pArray[i];
            }
        }
        
//实现IEnumerable
        public IEnumerator GetEnumerator()
        {
             
return _people.GetEnumerator();
        }
    }

 

 直接调用这些集合的GetEnumerator方法即可。

(实例代码来自MSDN,稍微做过一些修改)

 三、模式之末:

  两个关键点:

  1.提供一种方法顺序访问一个聚合对象中各个元素

  2.,不需暴露该对象的内部表示  

 

  以上为个人学习心得,请多指教。 

posted @ 2010-03-15 13:37 FrogTan 阅读(1321) 评论(0) 编辑 收藏
发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1686061 cl1/lFePqk8=