论坛首页 Java企业应用论坛

迭代子(Iterator)模式【行为模式第五篇】

浏览 2235 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (4)
作者 正文
   发表时间:2009-10-23   最后修改:2010-10-20
迭代子(Iterator)模式:
迭代子模式又叫游标(Cursor)模式,是对象的行为模式。迭代子模式可以顺序地访问一个聚集中的元素而不必暴露聚集的内部表象。

多个对象聚在一起形成的总体称之为聚集(Aggregate),聚集对象是能够包容一组对象的容器对象。聚集依赖于趋集结构的抽象化,具体复杂性
和多样性。数组就是最基本的聚集,也就是其他的java聚集对象的设计基础。
java聚集(Collection)对象是实现了共同的java.util.Collection接口的对象,是java语言对聚集概念的直接支持。

聚集对象必须提供适当的方法,允许客户端能够按照一个线性顺序遍历所有的元素对象,把元素对象提取出来或者删除掉等。一个使用聚集的系统
必然会使用这些方法操控聚集对象,因而在使用聚集的系统演化过程中,会出现两类问题:
(1)迭代逻辑没有改变,但是需要将一种聚集换成另一种聚集。因为不同的聚集具有不同的遍历接口,所以需要修改客户端代码,以便
     将已有的迭代调用换成新聚集对象所要求的接口。
(2)聚集不会改变,但是迭代方式需要改变。比如原来只需要读取元素和删除元素,但现在需要增加新的元素;或者原来的迭代仅仅遍历
     所有的元素,而现在则需要对元素加以过滤等。这时就只好修改聚集对象,修改已有的遍历方法,或者增加新的方法。
    
迭代子模式涉及到的几个角色:
1、抽象《迭代子》角色:此抽象角色定义出遍历元素所需的接口。
2、具体《迭代子》角色:此角色实现了Iterator接口,并保持迭代过程中的游标位置。
3、聚集角色:此抽象角色给出创建《迭代子》对象的接口。
4、具体聚集角色:实现了创建《迭代子》对象的接口,返回一个合适的具体《迭代子》实例。
5、客户端角色:持有对聚集及其《迭代子》对象的引用,调用迭代子对象的迭代接口,也有可能通过迭代子操作聚集元素的增加和删除。

宽接口和窄接口:
1、宽接口:如果一个聚集的接口提供了可以用来修改聚集元素的方法,这个接口就是所谓的宽接口
这种提供宽接口的聚集叫做白箱聚集。聚集对象向外界提供同样的宽接口。
2、窄接口:与上反之。
这种同时保证聚集对象的封装和迭代子功能的实现的方案叫做黑箱实现方案。
白箱聚集(外禀迭代子):
		//抽象聚集角色
		public abstract class Aggregate{
			//工厂方法返回一个迭代子
			public Iterator createIterator(){
				return null;
			}
		}
		
		//抽象迭代子角色
		public interface Iterator{
			//迭代方法:移动到第一个元素
			void first();
			
			//迭代方法:移动到下一个元素
			void next();
			
			//迭代方法:是否是最后一个元素
			boolean isDone();
			
			//迭代方法:返回当前元素
			Object currentItem(); 
		}
		
		//具体聚集角色
		public class ConcreteAggregate extends Aggregate{
			private Object[] obj = {
				"Monk Tang",
				"Monkey",
				"Pigsy",
				"Sandy",
				"Horse"
			};
			
			//工厂方法:返回一个迭代子对象
			public Iterator creteIterator(){
				return new ConcreteIterator(this);
			}
			
			//取值方法:向外界提供聚集元素
			public Object getElement(int index){
				if(index < obj.length){
					return obj[index];
				}else{
					return null;
				}
			}
			
			//取值方法,向外界提供聚集大小
			public int size(){
				return obj.length;
			}
		}
		
		如果一个对象的内部状态在对象被创建之后就不再变化,这个对象就称为不变对象。如果一个聚集对象的内部状态可以改变的话
		那么在迭代过程中,一旦聚集元素发生改变(比如一个元素被删除,或者一个新的元素被加进来),就会影响到迭代过程,使迭代无法
		给出正确的结果。
		
		//具体《迭代子》角色
		public class ConcreteIterator implements Iterator{
			private ConcreteAggregate agg;
			private int index = 0;
			private int size = 0;
			
			public ConcreteIterator(ConcreteAggregate agg){
				this.agg = agg;
				size = agg.size();
				index= 0;
			}
			
			public void first(){
				index= 0;
			}
			
			public void next(){
				if(index < size){
					index++;
				}
			}
			
			public boolean isDone(){
				return (index >= size);
			}
			
			public Object currentItem(){
				return agg.getElement(index);
			}
		}
		
		//客户端
		public class Client{
			private Iterator it;
			private Aggregate agg = new ConcreteAggregate();
			
			public void operation(){
				it = agg.createIterator();
				
				while(!it.isDone()){
					System.out.println(it.currentItem().toString());
					it.next();
				}
			}
			
			public static void main(String args[]){
				Client client = new Client();
				client.operation();
			}
		}


外禀迭代子的意义
客户端可以自行进行迭代,不一定非得需要一个迭代子对象。但是,迭代子对象和迭代模式会将迭代过程抽象化,将作为
迭代消费者的客户端与迭代负责人的迭代子责任分割开,使得两者可以独立演化。在聚集对象的种类发生变化,或者迭代
的方法发生改变时,迭代子作为一个中介层可以吸收变化的因素,而避免修改客户端或者聚集本身。
此外,如果系统需要同时针对几个不同的聚集对象进行迭代,而这些聚集对象所提供的遍历方法有所不同时,使用迭代子
模式和一个外界的迭代子对象是有意义的。具有同一迭代接口的不同迭代子对象处理具有不同遍历接口的聚集对象,使得
系统可以使用一个统一的迭代接口进行所有的迭代。

黑箱聚集(内禀迭代子)
一个黑箱聚集不向外部提供遍历自己元素的接口,因此,这些元素对象只可以被聚集内部成员访问。由于内禀迭代子恰好是聚集内部
的成员子类,因此,内禀迭代子对象是可以访问聚集的元素的。
//抽象聚集
		public abstract class Aggregate{
			//工厂方法,返还一个迭代子对象
			public abstract Iterator createIterator();
		}
		
		//抽象迭代子
		public interface Iterator{
			//迭代方法:移动到第一个元素
			void first();
			
			void next();
			
			boolean isDone();
			
			Object currentItem();
		}
		
		//具体聚集
		public class ConcreteAggregate extends Aggregate{
			private Object[] obj = {
				"Monk Tang",
				"Monkey",
				"Pigsy",
				"Sandy",
				"Horse"
			};
			
			public Iterator createIterator(){
				return new ConcreteIterator();
			}
			
			private class ConcreteIterator implements Iterator{
				private int currentIndex = 0;
				
				public void first(){
					currentIndex = 0;
				}
				
				public void next(){
					if(currentIndex < obj.length){
						currentIndex ++;
					}
				}
				
				public boolean isDone(){
					return (currentIndex == obj.length);
				}
				
				public Object currentItem(){
					return obj[currentIndex];
				}
			}
		}
		
		public class Client{
			private Iterator it;
			private Aggregate agg = new ConcreteAggregate();
			public void operation(){
				it = agg.createIterator();
				while(it.isDone){
					System.out.println(it.currentItem().toString());
					it.next();
				}
			}
			
			public static void main(String args[]){
				Client client = new Client();
				client.operation();
			}
		}


使用外禀迭代子和内禀迭代子的情况
一个外禀迭代子往往仅存储一个游标,因此如果有几个客户端同时进行迭代的话,那么可以使用几个外禀迭代子对象,由每一个迭代子
对象控制一个独立的游标。但是,外禀迭代子要求聚集对象向外界提供遍历方法,因此会破坏对聚集的封装。如果某一个客户端可以修
改聚集元素的话,迭代会给出不自恰的结果,甚至影响到系统其他部分的稳定性,造成系统崩溃。

使用外禀迭代子的一个重要理由是它可以被及个不同的方法和对象共同享用和控制。使用内禀迭代子的优点是它不破坏对象聚集的封装。
742P
静态迭代子和动态迭代子
静态迭代子由聚集对象创建,并持有聚集对象的一个快照,在生产后这个快照的内容就不在变化。客户端可以继续修改原聚集的内容
但是迭代子对象不会反映出聚集的新变化。
静态迭代子的好处是它的安全性和简易性,换言之,静态迭代子易于实现,不容易出现错误。但是由于静态迭代子将原聚集复制了一份
因此它的短处是对时间和内存资源的消耗。对大型的聚集对象来说。使用静态迭代子不是一个合适的选择。

动态迭代子则与静态迭代子完全相反,在迭代被产生后,迭代子保持者对聚集元素的引用,因此,任何对原聚集内容的修改都会在迭代
子对象上反映出来
FailFast的含义:如果当一个算法开始后,它的运算环境发生变化,使得算法无法进行必须的调整时,这个算法就应当立即发出故障信号。


迭代子模式的优点和缺点
1、迭代子模式的优点:
(1)、迭代子模式简化了聚集的界面。迭代子具备了一个遍历接口,这样聚集的接口就不必具备遍历接口。
(2)、每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。
       因此,一个聚集对象可以同时有几个迭代在进行之中。
(3)、由于遍历算法被封装在迭代子角色里面,因此迭代的算法可以独立于聚集角色变化。由于客户端拿到
       的是一个迭代子对象,因此,不必知道聚集对象的类型,就可以读取和遍历聚集对象。这样即便聚集对象
       的类型发生变化,也不会影响到客户端的遍历过程。
      
2、迭代子模式的缺点:
(1)、迭代子模式给客户端一个聚集被顺序化的错觉,因为大多数的情况下聚集的元素并没有确定的顺序,但是迭代
       必须以一定线性顺序进行。如果客户端误以为顺序是聚集本身具有的特性而过度依赖于聚集元素的顺序,很可
       能得出错误的结果。
(2)、迭代子给出的聚集元素没有类型特征。一般而言,迭代子给出的元素都是Object类型,因此,客户端必须具备
       这些元素类型的知识才能使用这些元素。  
一个例子:(关于搜索两警察的购物)
		//购物框抽象类
		public abstract class Purchase{
			private Vector elements = new Vector(5);
			
			//工厂方法:提供迭代子的实例
			public abstract Iterator createIterator();
			
			//聚集方法:将一个新元素增加到聚集的最后面
			public void append(Object anObj){
				elements.addElement(anObj);
			}
			//聚集方法:删除一个方法
			public void remove(Object anObj){
				elements.removeElement(anObj);
			}
			
			public Object currentItem(int n){
				return elements.elementAt(n);
			}
			
			public int count(){
				return elements.size();
			}
		}
		
		//具体聚集类
		public class PurchaseOfCopA extends Purchase{
			public PurchaseOfCopA(){}
			
			public Iterator createIterator(){
				return new ForwardIterator(this);
			}
		}
		
		public class PurchaseOfCopB extends Purchase{
			public PurchaseOfCopB(){}
			
			public Iterator createIterator(){
				return new BackwardIterator(this);
			}
		}
		
		//迭代子接口
		public interface Iterator{
			void first();
			
			void next();
			
			boolean isDone();
			
			Object currentItem();
		}
		
		//具体迭代子
		public class ForwardIterator implements Iterator{
			private int state;
			private Purchase obj;
			
			public ForwardIterator(Purchase anObj){
				obj = anObj;
			}
			
			public void first(){
				state = 0;
			}
			
			public void next(){
				if(!isDone()){
					state++;
				}
			}
			
			public boolean isDone(){
				if(state > obj.count()-1){
					return true;
				}
				return false;
			}
			
			public Object currentItem(){
				return obj.currentItem(state);
			}
		}
		
		public class BackwardIterator implements Iterator{
			private int state;
			private Purchase obj;
			
			public BackwardIterator(Purchase anObj){
				obj = anObj;
			}
			
			public void first(){
				state = obj.count()-1;
			}
			
			public void next(){
				if(!isDone()){
					state --;
				}
			}
			
			public boolean isDone(){
				if(state > 1){
					return true;
				}
				return false;
			}
			public Object currentItem(){
				return obj.currentItem(state);
			}
		}
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics