iterator:Net设计Iterator模式来源: 发布时间:星期四, 2009年2月12日 浏览:100次 评论:0
、模式概述 在面向对象设计时我们常常需要辨认对象职责理想状态下我们希望自己建立对象只具有个职责对象责任越少则该对象稳定性就越好受到约束也就越少职责分离可以最大限度地减少彼此的间耦合程度从而建立个松散耦合对象网络 职责分离要点是对被分离职责进行封装并以抽象方式建立起彼此的间关系在C#中我们往往将这些可能变化对象抽象为接口和抽象类从而将原来具体依赖改变为抽象依赖对象不再受制于具体实现细节这就代表他们是可被替换 要在设计上做到这点首先就要学会分辨职责学会分辨哪些职责是对象中可变以集合对象为例集合是个管理和组织数据对象数据结构这就表明集合首先应具备个基本属性就是集合能够存储数据这其中包含存储数据类型、存储空间大小、存储空间分配、以及存储方式和顺序不具备这些特点则该对象就不成其为集合对象也就是说上述这些属性是集合对象和身俱来是其密不可分职责然而集合对象除了能够存储数据外还必须提供访问其内部数据行为方式这是种遍历机制同时这种遍历方式或会根据区别情形提供区别实现如顺序遍历逆序遍历或是 2叉树结构中序、前序、后序遍历 现在我们已经分辨出集合对象拥有两个职责:是存储内部数据; 2是遍历内部数据从依赖性来看前者为集合对象根本属性属于生俱生亡俱亡关系;而后者既是可变化又是可分离因此我们将遍历行为分离出来抽象为个迭代器专门提供遍历集合内部数据对象行为这就是Iterator模式本质 如个列表对象List它提供了遍历列表各元素能力这种遍历行为可能包含两种:顺序和逆序遍历对于般List对象而言采用顺序遍历方式;而对于特定List对象如ReverseList则按照逆序遍历方式访问其内部数据如果不将存储数据和访问数据职责分离为实现ReverseList类就需要重写其父类List中所有用于遍历数据思路方法现在我们采用Iterator模式在List对象中分离出迭代器IListIterator那就只需要为这个继承自List对象ReverseList对象提供逆序迭代器ReverseListIterator就可以了如下面类图所示: 代码如下: public erface IListIterator { void First; void Last; bool MoveNext; } public SequenceListIterator:IListIterator { private List list = null; private index; public SequenceListIterator(List list) { index = -1; this.list = list; } public void First { index = 0; } public void Last { index = list.Count; } public bool MoveNext { index ; list.Count > index; } } public ReverseListIterator:IListIterator { private List list = null; private index; public ReverseListIterator(List list) [Page]{ index = list.Count; this.list = list; } public void First { index = list.Count - 1; } public void Last { index = 0; } public bool MoveNext { index --; index >= 0; } } public List { public virtual IListIterator CreateIterator { SequenceListIterator(this); } } public ReverseList:List { public override IListIterator CreateIterator { ReverseListIterator(this); } } 我们看到List类通过思路方法CreateIterator创建了SequenceListIterator对象使得List集合对象能够用顺序迭代方式遍历内部元素而要使ReverseList采用逆序方式遍历其内部元素则只需重写父类ListCreateIterator思路方法通过创建ReverseListIterator对象来建立集合和具体迭代器的间关系 2、 .Net中Iterator模式 在.Net中IEnumerator接口就扮演了Iterator模式中迭代器角色IEnumerator定义如下: public erface IEnumerator { bool MoveNext; Object Current {get; } void Re; } 该接口提供了遍历集合元素思路方法其中主要思路方法是MoveNext它将集合中元素下标移到下个元素如果集合中没有元素或已经移到了最后个则返回false能够提供元素遍历集合对象在.Net中都实现了IEnumerator接口 我们在使用.Net集合对象时会发现个思路方法GetEnumerator思路方法它类似前面提到List对象CreateIterator思路方法该思路方法返回类型是IEnumerator其内部则是创建并返回个具体迭代器对象正是通过这个思路方法建立了具体集合对象和迭代器的间关系 和通常Iterator模式实现区别.Net Framework将GetEnumerator思路方法单独抽象出来定义了接口IEnumerable: public erface IEnumerable { IEnumerator GetEnumerator; } IEnumerable接口就像迭代功能标识如果集合对象需要具备迭代遍历功能就必须实现该接口并在具体实现中创建和自身有关系具体迭代器对象而要获得该集合对象对应迭代器就可以通过该思路方法如: ArrayList al = ArrayList; IEnumerator iterator = al.GetEnumerator; 下面我就以ArrayList对象为例讨论下.Net中Iterator模式实现方式首先我们来看看ArrayList类定义: public ArrayList : IList, ICloneable 它分别实现了IList和ICloneable接口其中ICloneable接口提供了Clone思路方法和本文讨论内容无关略过不提IList接口则提供了和链表相关操作如AddRemove等这也是ArrayList和Array区别的处而IList接口又实现了ICollection接口 [Page]public erface IList : ICollection ICollection接口是所有集合类型公共接口它提供了获得集合长度和同步处理些思路方法不过在这里我们需要注意是它实现了IEnumerable接口: public erface ICollection : IEnumerable 追本溯源ArrayList类型间接地实现了IEnumerable接口在ArrayList中IEnumerable接口GetEnumerator思路方法实现代码如下: public virtual IEnumerator GetEnumerator { ArrayListEnumeratorSimple(this); } GetEnumerator思路方法是个虚思路方法这介绍说明我们可以自定义个集合类型继承ArrayList重写这个思路方法创建和返回区别IEnumerator对象从而实现区别遍历方式 为了实现ArrayList遍历功能采用Iterator模式结构如下图所示: 其中类ArrayListEnumeratorSimple实现如下所示: [Serializable] private ArrayListEnumeratorSimple : IEnumerator, ICloneable { // Methods ernal ArrayListEnumeratorSimple( ArrayList list) { this.list = list; this.index = -1; this.version = list._version; this.currentElement = list; } public object Clone{//实现略} public virtual bool MoveNext { (this.version != this.list._version) { throw InvalidOperationException(Environment.GetResourceString(\"InvalidOperation_EnumFailedVersion\")); } (this.index < (this.list.Count - 1)) { [Page] this.index; this.currentElement = this.list[this.index]; true; } this.currentElement = this.list; this.index = this.list.Count; false; } public virtual void Re { (this.version != this.list._version) { throw InvalidOperationException(Environment.GetResourceString(\"InvalidOperation_EnumFailedVersion\")); } this.currentElement = this.list ; this.index = -1; } // Properties public virtual object Current { get { object obj1 = this.currentElement; (obj1 != this.list) { [Page] obj1; } (this.index -1) { throw InvalidOperationException(Environment.GetResourceString(\"InvalidOperation_EnumNotStarted\")); } throw InvalidOperationException(Environment.GetResourceString (\"InvalidOperation_EnumEnded\")); } } // Fields private object currentElement; private index; private ArrayList list; private version; } ArrayListEnumeratorSimple实现了IEnumerator接口实现了MoveNext、Current、Re思路方法或属性该类是个私有类型其构造则被ernal修饰符限制在自定义构造中传入参数类型是ArrayList正是通过构造传递需要遍历ArrayList对象来完成MoveNext、Current、Re等操作 下面我们来看看如何通过IEnumerator来实现对ArrayList遍历操作 using ; using .Collections; using NUnit.Framework [TestFixture] public Tester { [Test] public void TestArrayList { ArrayList al = ArrayList; al.Add(5); al.Add(“Test”); IEnumerator e = al.GetEnumerator; e.MoveNext; Assert.AreEqual(5,e.Current); [Page] e.MoveNext; Assert.AreEqual(“Test”,e.Current); } } 而要遍历ArrayList内部所有元素思路方法也很简单: while (e.MoveNext) { Console.WriteLine(e.Current.); } 事实上为了用户更方便地遍历集合对象所有元素在C#中提供了foreach语句该语句实质正是通过IEnumeratorMoveNext思路方法来完成遍历下面语句和刚才那段代码是等价: foreach (object o in al) { Console.WriteLine(o.); } 为了验证foreach语句和迭代器关系我们来自定义个ReverseArrayList类要求遍历这个类内部元素时访问顺序是逆序要定义这样个类很简单只需要继承ArrayList类并重写GetEnumerator思路方法既可 public ReverseArrayList:ArrayList { public override IEnumerator GetEnumerator { ReverseArrayListEnumerator(this); } } 其中类ReverseArrayListEnumerator实现了接口IEnumerator它提供了逆序遍历迭代器: public ReverseArrayListEnumerator:IEnumerator { public ReverseArrayListEnumerator(ArrayList list) { this.list = list; this.index = list.Count; this.currentElement = list; } #region IEnumerator Members public virtual void Re { this.currentElement = this.list; this.index = this.list.Count; } public virtual object Current { [Page]get { object obj1 = this.currentElement; (obj1 != this.list) { obj1; } (this.index -1) { throw InvalidOperationException(\"Out of the Collection\"); } throw InvalidOperationException(\"Out of the Collection\"); } } public virtual bool MoveNext { (this.index > 0) { this.index--; this.currentElement = this.list[this.index]; true; } this.currentElement = this.list; [Page]this.index = 0; false; } #endregion private object currentElement; private index; private ArrayList list; } 注意ReverseArrayListEnumerator类和前面ArrayListEnumeratorSimple类区别主要在于遍历下个元素顺序ReverseArrayListEnumerator中MoveNext思路方法将下标往前移动以保证元素遍历逆序同时在构造化时将整个ArrayList对象元素个数赋予下标值: this.index = list.Count; 我们来比较下ArrayList和ReversieArrayList类的间通过foreach遍历后结果 [STAThread] public void Main( args) { ArrayList al = ArrayList; al.Add(1); al.Add(2); al.Add(3); ReverseArrayList ral = ReverseArrayList; ral.Add(1); ral.Add(2); ral.Add(3); Console.WriteLine(\"The Sequence ArrayList:\"); foreach ( i in al) { Console.Write(\"{0}\",i); } Console.WriteLine; Console.WriteLine(\"The Reverse ArrayList:\"); [Page] foreach ( i in ral) { Console.Write(\"{0}\",i); } Console.ReadLine; } 我们分别将数字123以同样顺序添加到ArrayList和ReverseArrayList对象中然后再通过foreach语句遍历输出其内部元素运行后很明显可以看到遍历ArrayList对象al其顺序为123;而ReverseArrayList则为321 由于我们应用Iterator模式将迭代器和集合对象完全分离所以即便我们完全修改了ReverseArrayList遍历方式实现ReverseArrayList也是非常容易同时它并没有影响到集合对象本身存储数据对象职能 0
相关文章读者评论发表评论 |