首先来看一下迭代器模式是干什么用的?

Java的人来说绝对不陌生。我们常常使用JDK提供的迭代接口进行java collection的遍历:
Iterator it = list.iterator();
while(it.hasNext()){
 //using “it.next();”do some businesss logic
}
       而这就是关于迭代器模式应用很好的例子。在软件构建过程中,集合对象内部结构常常变化各异。但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素;同时这种“透明遍历”也为“ 同一种算法在多种集合对象上进行操作”提供了可能。使用面向对象技术将这种遍历机制抽象为“迭代器对象”为“应对变化中的集合对象”提供了一种优雅的方法。


定义:提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。-------《设计模式》GOF


迭代器模式角色组成:
  1) 迭代器角色(Iterator):
迭代器角色负责定义访问和遍历元素的接口。
  2) 具体迭代器角色(Concrete Iterator):具体迭代器角色要实现迭代器接口,并要记录遍历中的当前位置。
  3) 容器角色(Container):容器角色负责提供创建具体迭代器角色的接口。
  4) 具体容器角色(Concrete Container):具体容器角色实现创建具体迭代器角色的接口——这个具体迭代器角色于该容器的结构相关。

结构图(Struct):             

                                             

Java程序员从笨鸟到菜鸟之(四十五)大话设计模式(九)迭代器模式和命令模式_java


        从结构上可以看出,迭代器模式在客户与容器之间加入了迭代器角色。迭代器角色的加入,就可以很好的避免容器内部细节的暴露,而且也使得设计符号“单一职责原则”。

注意,在迭代器模式中,具体迭代器角色和具体容器角色是耦合在一起的——遍历算法是与容器的内部细节紧密相关的。为了使客户程序从与具体迭代器角色耦合的困境中脱离出来,避免具体迭代器角色的更换给客户程序带来的修改,迭代器模式抽象了具体迭代器角色,使得客户程序更具一般性和重用性。这被称为多态迭代。


适用性: 

1.访问一个聚合对象的内容而无需暴露它的内部表示。

2.支持对聚合对象的多种遍历。

3.为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)。


具体的代码如下所示:

Iterator接口:

[java]    ​​view plain​​​   ​​copy​​​   ​​print​​​   ​​?​​    


1. package iterator;
2.
3. public interface Iterator{
4.
5. public Item first();
6.
7. public Item next();
8.
9. public boolean isDone();
10.
11. public Item currentItem();
12.
13. }


Controller类实现了Iterator接口。

  1. 从上面的示例中就可以看出,尽管我们没有显示的引用迭代器,但实质还是通过迭代器来遍历的。总地来说,迭代器模式就是分离了集合对象的迭代行为,抽象出一个迭代器类来负责,这样既可做到不暴露集合的内部结构,又可以让外部代码可以透明的访问集合内部的元素。迭代器模式在访问数组、集合、列表等数据时,尤其是数据库数据操作时,是非常普遍的应用,但由于它太普遍了,所以各种高级语言都对他进行了封装,所以反而给人感觉此模式本身不太常用了。
[java]    ​​view plain​​​   ​​copy​​​   ​​print​​​   ​​?​​    


1. package iterator;
2.
3. import java.util.Vector;
4.
5. public class Controller implements Iterator{
6.
7. private int current =0;
8.
9. Vector channel;
10.
11. public Controller(Vector v){
12.
13. channel = v;
14.
15. }
16.
17. public Item first(){
18.
19. 0;
20.
21. return (Item)channel.get(current);
22.
23. }
24.
25. public Item next(){
26.
27. current ++;
28.
29. return (Item)channel.get(current);
30.
31. }
32.
33. public Item currentItem(){
34.
35. return (Item)channel.get(current);
36.
37. }
38.
39. public boolean isDone(){
40.
41. return current>=channel.size()-1;
42.
43. }
44.
45. }


Television接口:
[java] ​​view plain​​​ ​​copy​​​ ​​print​​​ ​​?​​


1. package iterator;
2.
3. import java.util.Vector;
4.
5. public interface Television{
6.
7. public Iterator createIterator();
8.
9. public Vector getChannel();
10.
11. }


HaierTV类实现了Television接口。
[java] ​​view plain​​​ ​​copy​​​ ​​print​​​ ​​?​​


1. package iterator;
2.
3. import java.util.Vector;
4.
5. public class HaierTVimplements Television{
6.
7. private Vectorchannel;
8.
9. public HaierTV(){
10.
11. new Vector();
12.
13. new Item("channel 1"));
14.
15. new Item("channel 2"));
16.
17. new Item("channel 3"));
18.
19. new Item("channel 4"));
20.
21. new Item("channel 5"));
22.
23. new Item("channel 6"));
24.
25. new Item("channel 7"));
26.
27. }
28.
29. public Vector getChannel(){
30.
31. return channel;
32.
33. }
34.
35. public Iterator createIterator(){
36.
37. return new Controller(channel);
38.
39. }
40.
41. }


Client客户端:
[java] ​​view plain​​​ ​​copy​​​ ​​print​​​ ​​?​​


1. package iterator;
2.
3. public class Client{
4.
5. public static void main(String[] args){
6.
7. new HaierTV();
8.
9. Iterator it =tv.createIterator();
10.
11. System.out.println(it.first().getName());
12.
13. while(!it.isDone()){
14.
15. System.out.println(it.next().getName());
16.
17. }
18.
19. }
20.
21. }


Item类的接口:
[java] ​​view plain​​​ ​​copy​​​ ​​print​​​ ​​?​​


1. package iterator;
2.
3. public class Item{
4.
5. private Stringname;
6.
7. public Item(String aName){
8.
9. name = aName;
10.
11. }
12.
13. public String getName(){
14.
15. return name;
16.
17. }
18.
19. }

命令模式

定义:将一个请求封装为一个对象,从而使你不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。别名:动作(Action)、事务(Transaction)

command类。

命令模式角色组成      
1)        命令角色(Command):声明执行操作的接口。有java接口或者抽象类来实现。
2)        具体命令角色(Concrete Command):将一个接收者对象绑定于一个动作;调用接收者相应的操作,以实现命令角色声明的执行操作的接口。
3)        客户角色(Client):创建一个具体命令对象(并可以设定它的接收者)。
4)        请求者角色(Invoker):调用命令对象执行这个请求。
5)        接收者角色(Receiver):知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者。

Command模式应用范围
- 整个调用过程比较繁杂,或者存在多处这种调用。这时,使用Command类对该调用加以封装,便于功能的再利用。
- 调用前后需要对调用参数进行某些处理。
- 调用前后需要进行某些额外处理,比如日志,缓存,记录历史操作等。


结构如下所示:

                                                         

Java程序员从笨鸟到菜鸟之(四十五)大话设计模式(九)迭代器模式和命令模式_java_02

命令模式的优点:
解耦了发送者和接受者之间联系。 发送者调用一个操作,接受者接受请求执行相应的动作,因为使用Command模式解耦,发送者无需知道接受者任何接口。不少Command模式的代码都是针对图形界面的,它实际就是菜单命令,我们在一个下拉菜单选择一个命令时,然后会执行一些动作.将这些命令封装成在一个类中,然后用户(调用者)再对这个类进行操作,这就是Command模式,换句话说,本来用户(调用者)是直接调用这些命令的,如菜单上打开文档(调用者),就直接指向打开文档的代码,使用Command模式,就是在这两者之间增加一个中间者,将这种直接关系拗断,同时两者之间都隔离,基本没有关系了.显然这样做的好处是符合封装的特性,降低耦合度,Command是将对行为进行封装的典型模式,Factory是将创建进行封装的模式,从Command模式,我也发现设计模式一个"通病":好象喜欢将简单的问题复杂化, 喜欢在不同类中增加第三者,当然这样做有利于代码的健壮性 可维护性 还有复用性.

如何使用?
具体的Command模式代码各式各样,因为如何封装命令,不同系统,有不同的做法.下面事例是将命令封装在一个Collection的List中,任何对象一旦加入List中,实际上装入了一个封闭的黑盒中,对象的特性消失了,只有取出时,才有可能模糊的分辨出:

典型的Command模式需要有一个接口.接口中有一个统一的方法,这就是"将命令/请求封装为对象":

public interface Command {
public abstract void execute ( );
}

具体不同命令/请求代码是实现接口Command,下面有三个具体命令

public class Engineer implements Command {

public void execute( ) {
//do Engineer's command
}
} public class Programmer implements Command {

public void execute( ) {
//do programmer's command
}
}public class Politician implements Command {

public void execute( ) {
//do Politician's command
}
}

按照通常做法,我们就可以直接调用这三个Command,但是使用Command模式,我们要将他们封装起来,扔到黑盒子List里去:

public class producer{
public static List produceRequests() {
List queue = new ArrayList();
queue.add( new DomesticEngineer() );
queue.add( new Politician() );
queue.add( new Programmer() );
return queue;
}}

这三个命令进入List中后,已经失去了其外表特征,以后再取出,也可能无法分辨出谁是Engineer 谁是Programmer了,看下面客户端如何调用Command模式:

public class TestCommand {
public static void main(String[] args) {

List queue = Producer.produceRequests();
for (Iterator it = queue.iterator(); it.hasNext(); )

//客户端直接调用execute方法,无需知道被调用者的其它更多类的方法名。
((Command)it.next()).execute();}
}