本文来自:曹胜欢博客专栏。转载请注明出处:http://blog.csdn.net/csh624366188
首先来看一下迭代器模式是干什么用的?
迭代这个名词对于熟悉Java的人来说绝对不陌生。我们常常使用JDK提供的迭代接口进行javacollection的遍历:
Iteratorit=list.iterator();
while(it.hasNext()){
//using“it.next();”dosomebusinessslogic
}
而这就是关于迭代器模式应用很好的例子。在软件构建过程中,集合对象内部结构常常变化各异。但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素;同时这种“透明遍历”也为“同一种算法在多种集合对象上进行操作”提供了可能。使用面向对象技术将这种遍历机制抽象为“迭代器对象”为“应对变化中的集合对象”提供了一种优雅的方法。
定义:提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。-------《设计模式》GOF
迭代器模式角色组成:
1)迭代器角色(Iterator):迭代器角色负责定义访问和遍历元素的接口。
2)具体迭代器角色(ConcreteIterator):具体迭代器角色要实现迭代器接口,并要记录遍历中的当前位置。
3)容器角色(Container):容器角色负责提供创建具体迭代器角色的接口。
4)具体容器角色(ConcreteContainer):具体容器角色实现创建具体迭代器角色的接口——这个具体迭代器角色于该容器的结构相关。
结构图(Struct):
从结构上可以看出,迭代器模式在客户与容器之间加入了迭代器角色。迭代器角色的加入,就可以很好的避免容器内部细节的暴露,而且也使得设计符号“单一职责原则”。
注意,在迭代器模式中,具体迭代器角色和具体容器角色是耦合在一起的——遍历算法是与容器的内部细节紧密相关的。为了使客户程序从与具体迭代器角色耦合的困境中脱离出来,避免具体迭代器角色的更换给客户程序带来的修改,迭代器模式抽象了具体迭代器角色,使得客户程序更具一般性和重用性。这被称为多态迭代。
适用性:
1.访问一个聚合对象的内容而无需暴露它的内部表示。
2.支持对聚合对象的多种遍历。
3.为遍历不同的聚合结构提供一个统一的接口(即,支持多态迭代)。
具体的代码如下所示:
Iterator接口:
package iterator; public interface Iterator{ public Item first(); public Item next(); public boolean isDone(); public Item currentItem(); }
Controller类实现了Iterator接口。
package iterator; import java.util.Vector; public class Controller implements Iterator{ private int current =0; Vector channel; public Controller(Vector v){ channel = v; } public Item first(){ current = 0; return (Item)channel.get(current); } public Item next(){ current ++; return (Item)channel.get(current); } public Item currentItem(){ return (Item)channel.get(current); } public boolean isDone(){ return current>=channel.size()-1; } }
Television接口:
package iterator; import java.util.Vector; public interface Television{ public Iterator createIterator(); public Vector getChannel(); }
HaierTV类实现了Television接口。
package iterator; import java.util.Vector; public class HaierTVimplements Television{ private Vectorchannel; public HaierTV(){ channel =new Vector(); channel.addElement(new Item("channel 1")); channel.addElement(new Item("channel 2")); channel.addElement(new Item("channel 3")); channel.addElement(new Item("channel 4")); channel.addElement(new Item("channel 5")); channel.addElement(new Item("channel 6")); channel.addElement(new Item("channel 7")); } public Vector getChannel(){ return channel; } public Iterator createIterator(){ return new Controller(channel); } }
Client客户端:
package iterator; public class Client{ public static void main(String[] args){ Television tv =new HaierTV(); Iterator it =tv.createIterator(); System.out.println(it.first().getName()); while(!it.isDone()){ System.out.println(it.next().getName()); } } }
Item类的接口:
package iterator; public class Item{ private Stringname; public Item(String aName){ name = aName; } public String getName(){ return name; } } 从上面的示例中就可以看出,尽管我们没有显示的引用迭代器,但实质还是通过迭代器来遍历的。总地来说,迭代器模式就是分离了集合对象的迭代行为,抽象出一个迭代器类来负责,这样既可做到不暴露集合的内部结构,又可以让外部代码可以透明的访问集合内部的元素。迭代器模式在访问数组、集合、列表等数据时,尤其是数据库数据操作时,是非常普遍的应用,但由于它太普遍了,所以各种高级语言都对他进行了封装,所以反而给人感觉此模式本身不太常用了。
命令模式
定义:将一个请求封装为一个对象,从而使你不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。别名:动作(Action)、事务(Transaction)
面向对象的程序设计中,一个对象调用另一个对象,一般情况下的调用过程是:创建目标对象实例;设置调用参数;调用目标对象的方法。但在有些情况下有必要使用一个专门的类对这种调用过程加以封装,我们把这种专门的类称作command类。
命令模式角色组成
1)命令角色(Command):声明执行操作的接口。有java接口或者抽象类来实现。
2)具体命令角色(ConcreteCommand):将一个接收者对象绑定于一个动作;调用接收者相应的操作,以实现命令角色声明的执行操作的接口。
3)客户角色(Client):创建一个具体命令对象(并可以设定它的接收者)。
4)请求者角色(Invoker):调用命令对象执行这个请求。
5)接收者角色(Receiver):知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者。
Command模式应用范围
-整个调用过程比较繁杂,或者存在多处这种调用。这时,使用Command类对该调用加以封装,便于功能的再利用。
-调用前后需要对调用参数进行某些处理。
-调用前后需要进行某些额外处理,比如日志,缓存,记录历史操作等。
结构如下所示:
命令模式的优点:
解耦了发送者和接受者之间联系。发送者调用一个操作,接受者接受请求执行相应的动作,因为使用Command模式解耦,发送者无需知道接受者任何接口。不少Command模式的代码都是针对图形界面的,它实际就是菜单命令,我们在一个下拉菜单选择一个命令时,然后会执行一些动作.将这些命令封装成在一个类中,然后用户(调用者)再对这个类进行操作,这就是Command模式,换句话说,本来用户(调用者)是直接调用这些命令的,如菜单上打开文档(调用者),就直接指向打开文档的代码,使用Command模式,就是在这两者之间增加一个中间者,将这种直接关系拗断,同时两者之间都隔离,基本没有关系了.显然这样做的好处是符合封装的特性,降低耦合度,Command是将对行为进行封装的典型模式,Factory是将创建进行封装的模式,从Command模式,我也发现设计模式一个"通病":好象喜欢将简单的问题复杂化,喜欢在不同类中增加第三者,当然这样做有利于代码的健壮性可维护性还有复用性.
如何使用?
具体的Command模式代码各式各样,因为如何封装命令,不同系统,有不同的做法.下面事例是将命令封装在一个Collection的List中,任何对象一旦加入List中,实际上装入了一个封闭的黑盒中,对象的特性消失了,只有取出时,才有可能模糊的分辨出:
典型的Command模式需要有一个接口.接口中有一个统一的方法,这就是"将命令/请求封装为对象":
publicinterfaceCommand{ |
具体不同命令/请求代码是实现接口Command,下面有三个具体命令
publicclassEngineerimplementsCommand{ publicclassProgrammerimplementsCommand{ publicclassPoliticianimplementsCommand{ |
按照通常做法,我们就可以直接调用这三个Command,但是使用Command模式,我们要将他们封装起来,扔到黑盒子List里去:
publicclassproducer{ } |
这三个命令进入List中后,已经失去了其外表特征,以后再取出,也可能无法分辨出谁是Engineer谁是Programmer了,看下面客户端如何调用Command模式:
publicclassTestCommand{ } |