悄然无声的 Blog
不是我不明白,这世界变化快!

        前言:这两天学习 Iterator  模式,不知道是吕震宇老师没写这一篇,还是我没找到,就想到了把自己的学习体会拿出来与大家分享,我也是初学者,希望大家能提出意见。另外转载请注明作者和出处,毕竟花了快一周的时间才完成。

        一、两个概念

        1、聚集:所谓聚集就是一组数据集或者对象集,它可以通过循环来访问 。

        2、枚举器: 专门用来访问聚集的类,他包装了一定的方法,可以依次把聚集中的数据按照一定的顺序读出来。

        二、枚举器模式中出现的接口和类

        1、Aggregate 接口:抽象的聚集,通常只留有一个方法让子类去实现,这个方法的作用是获得一个枚举器对象,通常可以起名字为 GetIterator( )CreateIterator( ) 等等,或者干脆叫做 Iterator( ) 也行。在 C# 中,这个方法的名字叫做 GetEnumerator( ) ,这个我们到后面再讲。当然,在获得枚举器的同时,也要把自己 this(一组数据集)当作参数传给枚举器,想想也是,如果没有数据集,枚举器去枚举什么呢?

        2、ConcreteAggregate 类:具体的聚集,它的实例保存了一组数据集,同时它实现了 Aggregate 接口中唯一的那个方法,通常情况下他也会扩展出一些其他方法便于访问聚集中的数据,常见的有:访问某个位置数据的方法,可以叫做 GetElement(int index) 或者 GetItem(int index) 等等;获得聚集大小的方法,可以起名字为 GetLength( ) 或者 GetSize( ) 等等,全看自己喜好。

        3、Iterator 接口:抽象的枚举器,通常情况下会有三个方法留给子类去实现,他们分别是:Next( ) ,用来把指针移动到聚集中的下一个数据;HasNext( ) ,用来判断是否还有下一个数据;CurrentItem( ),返回当前指针所指位置的数据。也可以把 Next( ) CurrentItem( )
组成一个方法,在移动指针的同时返回一个数据。也可以有其他的实现方式,或者简单,或者复杂,也是全看个人需求。

        4、ConcreteIterator 类:具体的枚举器,它实现了上述的三个方法,通过不同的实现方式,我们可以获得不同的枚举方式,如顺序枚举、倒序枚举等等。当然,这个类的构造方法中会接受一个具体聚集参数,想想也是,如果没有这个数据集,他去枚举什么呢?这个参数就是他要去枚举的对象。

        5、被聚集的类,这个可以是任何类,它的许多个对象被存到聚集对象中才能形成一个真正的聚集,在我的例子中用的类叫 Book 类,它的四个实例即四本书形成一个聚集。下面看具体的代码吧。

        三、常规枚举器模式的代码实现

        这里用到“常规”这个词是指,这里的代码可以当作一个模版来用,不管你用的是 C# 还是 Java 或者是 Delphi ,均可以按照这个模版来实现枚举器模式。当然在 C# 和 Java 中,因为它们本身就支持枚举器模式,实现起来更方便一些,C#  的实现方法会在后面给出。

        我把客户端代码和模式类代码分别放在两个文件中。

        下面是模式类代码 Iterator.cs

using System;

namespace Iterator
{
    
/// <summary>
    
/// 一个普通的书籍类,它的许多实例可以形成一个聚集
    
/// </summary>

    public class Book
    
{
        
string name;
        
        
public Book(string name)
        
{
            
this.name=name;
        }


        
public string GetName()
        
{
            
return name;
        }

    }



    
/// <summary>
    
/// 抽象的聚集接口
    
/// </summary>

    public interface IAggregate
    
{
        IIterator GetIterator();
    }



    
/// <summary>
    
/// 书架,它是一个具体的聚集类,在这里是书的聚集
    
/// 所有聚集的共同点是,它会创建一个枚举器
    
/// 同时把自己当作参数传给枚举器,好让这个枚举器可以枚举自己
    
/// </summary>

    public class BookShelf:IAggregate
    
{
        
private Book[] books;
        
        
public BookShelf()
        
{
            books
=new Book[4];
            books[
0]=new Book("Around the World in 80 Days");
            books[
1]=new Book("Bible");
            books[
2]=new Book("Cinderella");
            books[
3]=new Book("Dady-long-legs");
        }


        
public Book GetElement(int index)
        
{
            
return books[index];
        }


        
public int GetLength()
        
{
            
return books.Length ;
        }


        
public IIterator GetIterator()
        
{
            
//  把自己  一个书架实例  传到一个具体的枚举器中,好让枚举器工作
            return new BookShelfIterator(this);
        }

    }



    
/// <summary>
    
/// 枚举器接口,抽象的,由它去枚举聚集
    
/// </summary>

    public interface IIterator
    
{
        
bool HasNext();
        Object CurrentItem();
        
void Next();
    }



    
/// <summary>
    
/// 具体的枚举器,在这里是一个书架 
    
/// 所有枚举器的特点是,他会在构造方法中得到一个具体的聚集实例,并用一定的方法去枚举他
    
/// </summary>

    public class BookShelfIterator:IIterator
    
{
        
private BookShelf bookShelf;
        
private int index;
        
private int size;

        
public BookShelfIterator(BookShelf bookShelf)
        
{
            
//获得一个具体聚集,去枚举他
            this.bookShelf=bookShelf;
            index
=0;
            size
=bookShelf.GetLength();
        }


        
public bool HasNext()
        
{
            
if(index<size)        return true;
            
else return false;
        }


        
public Object CurrentItem()
        
{
            
return bookShelf.GetElement(index);
        }


        
public void Next()
        
{
            
if(index<size) index++;
        }

    }

}

        下面是客户端代码:Client.cs

using System;


namespace Iterator
{
    
class MainClass
    
{
        [STAThread]
        
static void Main(string[] args)
        
{
            IIterator iterator;
            IAggregate agg
=new BookShelf();//创建一个书架,书架上已经存了四本书
            
            iterator
=agg.GetIterator();//创建一个可以枚举这个书架的枚举器
            while(iterator.HasNext())
            
{
                Book book
=(Book)iterator.CurrentItem();
                Console.WriteLine(book.GetName());
                iterator.Next();
            }

            Console.Read();
        }

    }

}

        四、在 C#中枚举器模式的实现——使用 foreach 语句

        在 C# 中,因为 Aggregate 接口和 Iterator 接口,已经存在了,我们直接去实现他就可以了。 C# 中的Aggregate 接口叫做 IEnumerable,翻译过来是可以被枚举的,什么可以被枚举?当然就是聚集了。Iterator 接口叫做 IEnumerator ,是不是换汤不换药?如果上面的代码您已经看懂了,下面的代码应该也难不住你,我就不多说了,有注释。

        我把客户端代码和模式类代码分别放在两个文件中。

        下面是模式类代码 Iterator.cs

using System;
using System.Collections ;

namespace Iterator
{
    
public class Book
    


    
public class BookShelf:IEnumerable
    
{
        
private Book[] books;
        
        
public BookShelf()
        
{
            books
=new Book[4];
            books[
0]=new Book("Around the World in 80 Days");
            books[
1]=new Book("Bible");
            books[
2]=new Book("Cinderella");
            books[
3]=new Book("Dady-long-legs");
        }


        
public Book GetElement(int index)
        
{
            
return books[index];
        }


        
public int Length
        
{
            
get
            
{
                
return books.Length ;
            }

        }

    
        
//实现 IEnumerable 接口的方法,得到一个枚举器,并把自己作为参数传给枚举器
        public IEnumerator GetEnumerator()
        
{
            Console.WriteLine(
"得到枚举器!");
            
return new BookShelfIterator(this);;
        }


    }

    

    
public class BookShelfIterator:IEnumerator
    
{
        
private BookShelf bookShelf;
        
private int index;

        
//得到一个聚集对象,可以去枚举他
        public BookShelfIterator(BookShelf bookShelf)
        
{
            
this.bookShelf=bookShelf;
            index
=-1;
        }

    
        
//实现 IEnumerator 的方法,将指针移动到下一个
        public bool MoveNext()
        
{
            
if(index<bookShelf.Length -1
            
{
                Console.Write(
"MoveNext->");
                index
++;
                
return true;
            }

            
else return false;
        }

    
        
//实现 IEnumerator 的方法,将指针重置到起始位置
        public void Reset()
        
{
            index
=-1;
            Console.WriteLine(
"Reset!");
        }

    
        
//实现 IEnumerator 的属性,返回当前指针所指的数据
        public object Current
        
{
            
get
            
{
                Console.Write(
"GetCurrent->");
                
return bookShelf.GetElement(index);
            }

        }

    }

}

        下面是客户端代码:Client.cs      

using System;

namespace Iterator
{
    
class MainClass
    
{
        [STAThread]
        
static void Main(string[] args)
        
{
            BookShelf agg
=new BookShelf();//创建一个书架,书架上已经存了四本书
            foreach(Book book in agg)  Console.WriteLine(book.Name);    
            Console.Read();
        }

    }

}

        关于这两段代码,用分步运行就可以搞清楚 foreach 是怎么运作的了。但我还是要解释一下。

        1、首先 in 后面的变量一定得是个聚集,即必须返回一个 IEnumerable 类型的对象。在这里我们直接给它了,就是 agg 。但当你想实现多种枚举方式的时候,就必须知道这一点。所以再重复一遍,in 后面的变量必须是一个 IEnumerable 类型的对象。

        2、当循环开始启动时, 首先运行的是 in 后面那个聚集对象的 GetEnumerator( ) 方法,它返回一个枚举器,并把聚集自身当作参数传给枚举器。这个枚举器到底返回给谁了?我不知道,但是有一点我很清楚,这个方法 return 的是哪个枚举器的实例, foreach 就会用哪个枚举器的方式去枚举数据。当你想实现多种枚举方式的时候,你必须知道这一点。这里我们只用到了一个枚举器,后面我们会举一个两个枚举器的例子。

        3、当确定使用哪个枚举器以后, foreach  接着会调用这个枚举器的 MoveNext( ) 方法,什么都没干先移动一下?很奇怪吧?就是这样!所以注意 index 要从 -1 开始。

        4、每 MoveNext( )  下,接着就调用  Current 属性,返回当前指针指向的数据。

        5、当 MoveNext( ) 返回 false 时,一个  foreach  过程就结束了。

运行效果如下图:

        五、一个聚集实现多个枚举器——使用 foreach 语句

        这一部分我不作讲解了,除了上面的功能外,多实现的一个倒序的枚举器,如果你能看懂上一部分的5点,这里的代码就很容易理解了。在C# 2.0中还有更好地实现方式,但是不适合用于讲解模式,有兴趣的朋友可以自己去查一些资料。另外总觉得这段代码应该不是很漂亮,有修改的余地,请高手赐教。

 

下面是模式类代码 Iterator.cs
using System;
using System.Collections ;

namespace Iterator
{
    
/// <summary>
    
/// 
    
/// </summary>

    public class Book
    
{
        
string name;
        
        
public Book(string name)
        
{
            
this.name=name;
        }


        
public string Name
        
{
            
get
            
{
                
return name;
            }

        }

    }



    
/// <summary>
    
/// 
    
/// </summary>

    public class BookShelf:IEnumerable
    
{
        
private Book[] books;
        
private IEnumerator iterator;
        
        
public BookShelf()
        
{
            books
=new Book[4];
            books[
0]=new Book("Around the World in 80 Days");
            books[
1]=new Book("Bible");
            books[
2]=new Book("Cinderella");
            books[
3]=new Book("Dady-long-legs");

            iterator
=new DefaulIterator(this);
        }


        
public Book GetElement(int index)
        
{
            
return books[index];
        }


        
public int Length
        
{
            
get
            
{
                
return books.Length ;
            }

        }

    
        
public IEnumerator GetEnumerator()
        
{
            Console.WriteLine(
"得到枚举器!");
            
return iterator;
        }

        
        
public IEnumerable GetOrderBooks()
        
{
            iterator
=new DefaulIterator(this);
            
return  this;
        }



        
public IEnumerable GetReverseBooks()
        
{
            iterator
=new OtherIterator(this);
            
return  this;
        }

    }

    

    
/// <summary>
    
///
    
/// </summary>

    public class DefaulIterator:IEnumerator
    
{
        
private BookShelf bookShelf;
        
private int index;

        
public DefaulIterator(BookShelf bookShelf)
        
{
            
//获得一个具体聚集,去枚举他
            this.bookShelf=bookShelf;
            index
=-1;
        }

    
        
public bool MoveNext()
        
{
            
if(index<bookShelf.Length -1
            
{
                Console.Write(
"MoveNext->");
                index
++;
                
return true;
            }

            
else return false;
        }

    
        
public void Reset()
        
{
            index
=-1;
            Console.WriteLine(
"Reset!");
        }

    
        
public object Current
        
{
            
get
            
{
                Console.Write(
"GetCurrent->");
                
return bookShelf.GetElement(index);
            }

        }

    }




    
/// <summary>
    
///
    
/// </summary>

    public class OtherIterator:IEnumerator
    
{
        
private BookShelf bookShelf;
        
private int index;

        
public OtherIterator(BookShelf bookShelf)
        
{
            
//获得一个具体聚集,去枚举他
            this.bookShelf=bookShelf;
            index
=4;
        }

    
        
public bool MoveNext()
        
{
            
if(index>0
            
{
                Console.Write(
"MoveNext->");
                index
--;
                
return true;
            }

            
else return false;
        }

    
        
public void Reset()
        
{
            index
=4;
            Console.WriteLine(
"Reset!");
        }

    
        
public object Current
        
{
            
get
            
{
                Console.Write(
"GetCurrent->");
                
return bookShelf.GetElement(index);
            }

        }

    }



}

下面是客户端代码:Client.cs      

using System;

namespace Iterator
{
    
class MainClass
    
{
        [STAThread]
        
static void Main(string[] args)
        
{

            BookShelf agg
=new BookShelf();//创建一个书架,书架上已经存了四本书

            
foreach(Book book in agg.GetOrderBooks())  Console.WriteLine(book.Name);
            
foreach(Book book in agg.GetReverseBooks())  Console.WriteLine(book.Name);

            Console.Read();
        }

    }

}

运行效果如下图:

 

 

posted on 2005-07-28 13:15  左洸  阅读(3182)  评论(6编辑  收藏  举报