The NoteBook of EricKong

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  611 Posts :: 1 Stories :: 190 Comments :: 0 Trackbacks

#

设计模式解读之一: 策略模式
1. 模式定义

把会变化的内容取出并封装起来,以便以后可以轻易地改动或扩充部分,而不影响不需要变化的其他部分;

2. 问题缘起
当涉及至代码维护时,为了复用目的而使用继承,结局并不完美。对父类的修改,会影响到子类型。在超类中增加的方法,会导致子类型有该方法,甚至连那些不该具备该方法的子类型也无法免除。示例,一个鸭子类型:

public abstract class Duck {
    //所有的鸭子均会叫以及游泳,所以父类中处理这部分代码
    public void quack() {
        System.out.println("Quack");
    }

    public void swim() {
        System.out.println("All ducks float, even decoys.");      
    }

    //因为每种鸭子的外观是不同的,所以父类中该方法是抽象的,由子类型自己完成。
    public abstract void display();
}

public class MallardDuck extends Duck {
    //野鸭外观显示为绿头
    public void display() {
        System.out.println("Green head.");
    }
}

public class RedHeadDuck extends Duck {
    //红头鸭显示为红头
    public void display() {
        System.out.println("Red head.");
    }
}

public class RubberDuck extends Duck {
   
    //橡皮鸭叫声为吱吱叫,所以重写父类以改写行为
    public void quack() {
        System.out.println("Squeak");
    }

    //橡皮鸭显示为黄头
    public void display() {
        System.out.println("Yellow head.");
    }
}

                    上述代码,初始实现得非常好。现在我们如果给Duck.java中加入fly()方法的话,那么在子类型中均有了该方法,于是我们看到了会飞的橡皮鸭子,你看过吗?当然,我们可以在子类中通过空实现重写该方法以解决该方法对于子类型的影响。但是父类中再增加其它的方法呢?

通过继承在父类中提供行为,会导致以下缺点:

a. 代码在多个子类中重复;
b. 运行时的行为不容易改变;
c. 改变会牵一发动全身,造成部分子类型不想要的改变;

好啦,还是刚才鸭子的例子,你也许想到使用接口,将飞的行为、叫的行为定义为接口,然后让Duck的各种子类型实现这些接口。这时侯代码类似于:

public abstract class Duck {
    //将变化的行为 fly() 以及quake()从Duck类中分离出去定义形成接口,有需求的子类中自行去实现

    public void swim() {
        System.out.println("All ducks float, even decoys.");      
    }

    public abstract void display();
}

//变化的 fly() 行为定义形成的接口
public interface FlyBehavior {
    void fly();
}

//变化的 quack() 行为定义形成的接口
public interface QuackBehavior {
    void quack();
}

//野鸭子会飞以及叫,所以实现接口  FlyBehavior, QuackBehavior
public class MallardDuck extends Duck implements FlyBehavior, QuackBehavior{
    public void display() {
        System.out.println("Green head.");
    }

    public void fly() {
        System.out.println("Fly.");              
    }

    public void quack() {
        System.out.println("Quack.");              
    }
}

//红头鸭子会飞以及叫,所以也实现接口  FlyBehavior, QuackBehavior
public class RedHeadDuck extends Duck implements FlyBehavior, QuackBehavior{
    public void display() {
        System.out.println("Red head.");
    }  

    public void fly() {
        System.out.println("Fly.");              
    }

    public void quack() {
        System.out.println("Quack.");              
    }  
}

//橡皮鸭不会飞,但会吱吱叫,所以只实现接口QuackBehavior
public class RubberDuck extends Duck implements QuackBehavior{
    //橡皮鸭叫声为吱吱叫
    public void quack() {
        System.out.println("Squeak");
    }

    //橡皮鸭显示为黄头
    public void display() {
        System.out.println("Yellow head.");
    }
}

上述代码虽然解决了一部分问题,让子类型可以有选择地提供一些行为(例如 fly() 方法将不会出现在橡皮鸭中).但我们也看到,野鸭子MallardDuck.java和红头鸭子RedHeadDuck.java的一些相同行为代码不能得到重复使用。很大程度上这是从一个火坑跳到另一个火坑。

在一段程序之后,让我们从细节中跳出来,关注一些共性问题。不管使用什么语言,构建什么应用,在软件开发上,一直伴随着的不变的真理是:需要一直在变化。不管当初软件设计得多好,一段时间之后,总是需要成长与改变,否则软件就会死亡。
    我们知道,继承在某种程度上可以实现代码重用,但是父类(例如鸭子类Duck)的行为在子类型中是不断变化的,让所有子类型都有这些行为是不恰当的。我们可以将这些行为定义为接口,让Duck的各种子类型去实现,但接口不具有实现代码,所以实现接口无法达到代码复用。这意味着,当我们需要修改某个行为,必须往下追踪并在每一个定义此行为的类中修改它,一不小心,会造成新的错误。

设计原则:把应用中变化的地方独立出来,不要和那些不需要变化的代码混在一起。这样代码变化引起的不经意后果变少,系统变得更有弹性。

按照上述设计原则,我们重新审视之前的Duck代码。

1) 分开变化的内容和不变的内容

Duck类中的行为 fly(), quack(), 每个子类型可能有自己特有的表现,这就是所谓的变化的内容。
Duck类中的行为 swim() 每个子类型的表现均相同,这就是所谓不变的内容。

我们将变化的内容从Duck()类中剥离出来单独定义形成接口以及一系列的实现类型。将变化的内容定义形成接口可实现变化内容和不变内容的剥离。其实现类型可实现变化内容的重用。这些实现类并非Duck.java的子类型,而是专门的一组实现类,称之为"行为类"。由行为类而不是Duck.java的子类型来实现接口。这样,才能保证变化的行为独立于不变的内容。于是我们有:

变化的内容:

//变化的 fly() 行为定义形成的接口
public interface FlyBehavior {
    void fly();
}

//变化的 fly() 行为的实现类之一
public class FlyWithWings implements FlyBehavior {
    public void fly() {
        System.out.println("I'm flying.");
    }
}

//变化的 fly() 行为的实现类之二
public class FlyNoWay implements FlyBehavior {
    public void fly() {
        System.out.println("I can't fly.");
    }
}

-----------------------------------------------------------------

//变化的 quack() 行为定义形成的接口
public interface QuackBehavior {
    void quack();
}

//变化的 quack() 行为实现类之一
public class Quack implements QuackBehavior {
    public void quack() {
        System.out.println("Quack");
    }
}

//变化的 quack() 行为实现类之二
public class Squeak implements QuackBehavior {
    public void quack() {
        System.out.println("Squeak.");
    }
}

//变化的 quack() 行为实现类之三
public class MuteQuack implements QuackBehavior {
    public void quack() {
        System.out.println("<< Slience >>");
    }
}

通过以上设计,fly()行为以及quack()行为已经和Duck.java没有什么关系,可以充分得到复用。而且我们很容易增加新的行为,既不影响现有的行为,也不影响Duck.java。但是,大家可能有个疑问,就是在面向对象中行为不是体现为方法吗?为什么现在被定义形成类(例如Squeak.java)?在OO中,类代表的"东西"一般是既有状态(实例变量)又有方法。只是在本例中碰巧"东西"是个行为。既使是行为,也有属性及方法,例如飞行行为,也需要一些属性记录飞行的状态,如飞行高度、速度等。

2) 整合变化的内容和不变的内容

Duck.java将 fly()以及quack()的行为委拖给行为类处理。

不变的内容:

public abstract class Duck {

    //将行为类声明为接口类型,降低对行为实现类型的依赖
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;

    public void performFly() {
    //不自行处理fly()行为,而是委拖给引用flyBehavior所指向的行为对象
        flyBehavior.fly();
    }

    public void performQuack() {
        quackBehavior.quack();
    }

    public void swim() {
        System.out.println("All ducks float, even decoys.");      
    }

    public abstract void display();
}

Duck.java不关心如何进行 fly()以及quack(), 这些细节交由具体的行为类完成。

public class MallardDuck extends Duck{
    public MallardDuck() {
        flyBehavior=new FlyWithWings();
        quackBehavior=new Quack();      
    }

    public void display() {
        System.out.println("Green head.");
    }
}

测试类:

public class DuckTest {
    public static void main(String[] args) {
        Duck duck=new MallardDuck();
        duck.performFly();
        duck.performQuack();      
    }
}

在Duck.java子类型MallardDuck.java的构造方法中,直接实例化行为类型,在编译的时侯便指定具体行为类型。当然,我们可以:

1) 我们可以通过工厂模式或其它模式进一步解藕(可参考后续模式讲解);
2) 或做到在运行时动态地改变行为。

3) 动态设定行为

在父类Duck.java中增加设定行为类型的setter方法,接受行为类型对象的参数传入。为了降藕,行为参数被声明为接口类型。这样,既便在运行时,也可以通过调用这二个方法以改变行为。

public abstract class Duck {
    //在刚才Duck.java中加入以下二个方法。
    public void setFlyBehavior(FlyBehavior flyBehavior) {
        this.flyBehavior=flyBehavior;
    }

    public void setQuackBehavior(QuackBehavior quackBehavior) {
        this.quackBehavior=quackBehavior;
    }

//其它方法同,省略...
}

测试类:

public class DuckTest {
    public static void main(String[] args) {
        Duck duck=new MallardDuck();
        duck.performFly();
        duck.performQuack();
        duck.setFlyBehavior(new FlyNoWay());
        duck.performFly();
    }
}

如果,我们要加上火箭助力的飞行行为,只需再新建FlyBehavior.java接口的实现类型。而子类型可通过调用setQuackBehavior(...)方法动态改变。至此,在Duck.java增加新的行为给我们代码所带来的困绕已不复存在。

该是总结的时侯了,让我们从代码的水中浮出来,做一只在水面上自由游动的鸭子吧:

3.  解决方案

MallardDuck 继承  Duck抽象类;          -> 不变的内容
FlyWithWings 实现 FlyBehavior接口;     -> 变化的内容,行为或算法
在Duck.java提供setter方法以装配关系;    -> 动态设定行为

以上就是策略模式的实现三步曲。接下来,让我们透过步骤看本质:

1) 初始,我们通过继承实现行为的重用,导致了代码的维护问题。          -> 继承, is a
2) 接着,我们将行为剥离成单独的类型并声明为不变内容的实例变量并通过  -> 组合, has a
setter方法以装配关系;

继承,可以实现静态代码的复用;组合,可以实现代码的弹性维护;使用组合代替继承,可以使代码更好地适应软件开发完后的需求变化。

策略模式的本质:少用继承,多用组合
posted @ 2010-10-16 21:21 Eric_jiang 阅读(209) | 评论 (0)编辑 收藏

<!-- dataSource config -->
    <bean id="dataSource"
        class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <!-- 解决数据库中出现乱码的方法在数据库名字后面加?useUnicode=true&amp;characterEncoding=utf-8-->
        <property name="url" value="jdbc:mysql://localhost:3306/game?useUnicode=true&amp;characterEncoding=utf-8" />
        <property name="username" value="root" />
        <property name="password" value="root" />
    </bean>
posted @ 2010-10-10 09:55 Eric_jiang 阅读(1037) | 评论 (0)编辑 收藏

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
            destroy-method="close">
            <property name="driverClass">
                <value>com.microsoft.sqlserver.jdbc.SQLServerDriver</value>
            </property>
            <property name="jdbcUrl">
                <value>jdbc:sqlserver://localhost:1433;databaseName=databaseName</value>
            </property>
            <property name="user">
                <value>UserName</value>
            </property>
            <property name="password">
                <value>123456</value>
            </property>
            <property name="minPoolSize">
                <value>15</value>
            </property>
            <property name="acquireIncrement">
                <value>5</value>
            </property>
            <property name="maxPoolSize">
                <value>25</value>
            </property>
        </bean>
posted @ 2010-10-10 09:52 Eric_jiang 阅读(4010) | 评论 (1)编辑 收藏

建立连接
既然是通信,当然第一步就是要建立连接啦。我们还是用最简单的看代码的方式来开始我们的连接。

==========================================
==服务端代码:Jserver1.java
==========================================

import java.net.*;
import java.io.*;
public class Jserver1 {
public static void main(String[] args) {
ServerSocket server=null;
try{
   server=new ServerSocket(719);//在端口719建立一个服务器套接字对象
  System.out.println("服务端等待");
  while(true){
   System.out.println("等待客户机");
   Socket newSocket=server.accept();//客户端向我们建立的服务器套接字发送连接请求,则向下执行
   System.out.println("已与客户机连接");
  }
}catch(IOException ie)
{
  System.out.println(ie);
}finally{
  try
  {
   if(server!=null) server.close();//关闭服务器套接字。为什么要用finally关键字来做这个事呢?HOHO thinking in java里大师已经说得很清楚了,就是无论出现什么异常,都保证我们的服务端套接字能被关闭。
  }catch(IOException ie){}
}
}
 嘎嘎,怎么样 注释写得够清楚了吧?OK 让我们编译运行,得到的结果应该如图:

OK 再让我们来看看客户端的代码是怎么样的:
==========================================
==客户端代码:Jclient1.mxml
===================================================


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creati f width="349" height="326">
<mx:Script>
  <![CDATA[
  import flash.net.Socket;
  private var socket:Socket;
  internal function initApp():void
  {
   socket=new Socket();//创建Socket对象
   socket.connect("localhost",719);  //连接服务器
   socket.addEventListener(Event.CONNECT,connectFun);//监听是否连接上服务器
   socket.addEventListener(Event.CLOSE,closeFun);//监听套接字连接是否关闭
  }
  private function connectFun(event:Event):void
  {
   jText.text+="已经成功连接到服务器!\n";
  }

  private function closeFun(event:Event):void
  {
   jText.text+="和服务器断开!\n"
  }
  ]]>
</mx:Script>
<mx:TextArea x="10" y="10" width="327" height="309" id="jText"/>
</mx:Application>

界面非常简单啦,其实就是一个TextArea来显示连接的结果而已,运行的结果如下图(注意,服务端也应该在运行!):

在as3.0中,所有和网络通信有关的类都位于flash.net包中,这里使用的是Socket类对象。
==========================================
==我们来详细说明一下上面用到的Socket类:
==========================================

Socket对象的常用创建方法有下面2种:
var socket:Socket=new Socket();//例一
或者
var socket:Socket=new Socket("localhost",719);//例二.这条语句设置了服务器地址为localhost 端口是719

当指定了了服务器地址和端口,Socket对象将自动开始连接服务器。如果不指定,则需要条用connect方法才开始执行连接动作,意思就是,例二的代码和下面的代码是等效的:

var socket:Socket=new Socket();
socket.connect("localhost",719);
完成连接动作后,接下来要获取服务端得返回信息。
Socket对象和URLLOADER啊之类的对象都是一样,利用事件机制来处理服务器端的信息,我们只要给Socket对象添加相关的事件监听函数就可以捕捉到服务器端的信息,Socket对象的事件主要有:
1 Event.CLOSE 连接中断事件。
2 Event.CONNECT 连接状态事件,表示已经成功连接了服务器。
3 IOErrorEvent.IO_ERROR 信息传递错误事件,一般是由服务器地址错误引起的。
4 ProgressEvent.SOCKET_DATA 服务器信息事件,当收到服务器的新信息时被触发。

==========================================

哈哈,经过上面的代码,我们的服务端MM和客户端GG终于通过Socket这个缘分宿命的相遇了!接下来会发生什么?我们继续往下看~~
三、第一封情书(客户端发送消息,服务端接受消息)

 

客户端GG在遇到服务端MM以后,终日变得茶饭不思,在折磨掉了无数根头发以后,客户端GG终于下定决心,要向服务端MM送出第一封情书啦!

既然是客户端GG送出情书,那我们先来看他到底是怎么做的:
==========================================
==客户端代码:Jclient2.mxml
==========================================

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" fontSize="12" creationComplete="initApp()">
<mx:Script>
<![CDATA[
import flash.net.Socket;
import flash.utils.ByteArray;

private var socket:Socket=new Socket(); //定义Socket,准备好情书的信封
//初始化程序
internal function initApp():void
{
socket.connect("localhost",719); //连接服务器
socket.addEventListener(Event.CONNECT,funConnect); //监听是否连接
socket.addEventListener(Event.CLOSE,funClose); //监听连接关闭
}
internal function funConnect(event:Event):void
{
myText.text+="连接已建立 \n";
}
internal function funClose(event:Event):void
{
myText.text+="连接已关闭 \n";
}

internal function sendMessage(msg:String):void//发送数据对应按钮click事件
{
var message:ByteArray=new ByteArray();//新建一个ByteArray存放数据
message.writeUTFBytes(msg +"\r\n");//写入数据,writeUTFBytes方法,以utf-8格式传数据避免中文乱码
socket.writeBytes(message);  //写入Socket的缓冲区
socket.flush();  //调用flush方法发送信息
myText.text+=msg+"\r\n";  //在客户端屏幕上输出发送的内容
myInput.text=""; //清空发言框
}
]]>
</mx:Script>
<mx:TextArea x="10" y="10" width="703" height="263" id="loveText"/>
<mx:TextInput x="10" y="297" width="605" id="loveInput"/>
<mx:Button x="648" y="297" label="发送情书" id="sendBtn" click="sendMessage(loveInput.text)"/>
</mx:Application>

嘎嘎,情书的做法就上面那面简单,注释已经写得很清楚了,就不多说了。

OK,客户端GG的情书倒是送出去了,我们就来看看服务端MM是怎么接受这封情书的呢?


OK,客户端GG的情书倒是送出去了,我们就来看看服务端MM是怎么接受这封情书的呢?
==========================================
==服务端代码:Jserver2.java
==========================================

import java.net.*;
import java.io.*;
public class Jserver2{
private BufferedReader reader; //负责输入
private ServerSocket server;   //服务器套接字
private Socket socket;      //套接字
public Server2(){}  //缺省构造函数
void startServer()  //启动服务器
{
try
{
server=new ServerSocket(719);    //创建服务器套接字
System.out.println("服务器套接字建立完毕");
while(true)
{
  System.out.println("等待客户端GG");
  socket=server.accept(); //若客户端GG提出连接请求,与socket连接
  System.out.println("完成与客户端的连接");
reader=new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-8")); //获取socket输入流,“utf-8”这个编码设置是为了更好显示中文
getMessage();//读取来自客户端的数据,并输出至画面上
}
}catch(Exception e)
{
System.out.println(e);
}finally{
try
{
if(server!=null) server.close();//关闭服务器套接字。
}catch(IOException ie){}
}
}
void getMessage()  //读取来自套接字的信息
{
try
{
while(true)    //循环
{
System.out.println("客户端GG说:"+reader.readLine());
}
}catch(Exception e){}
finally{
System.out.println("客户端中断连接");
try
{
if(reader!=null) reader.close(); //关闭套接字的输入流
if(socket!=null) socket.close();  //关闭套接字
reader=null;
socket=null;
}catch(Exception e){}
}
}
public static void main(String[] args)
{
Server2 server=new Server2();
server.startServer();
}

}

哈哈,我们运行来看看,我们的客户端GG的情书能否顺利到达服务端MM的手中呢?
运行结果如下:


HOHO 看样子我们的客户端GG的情书,服务端MM是顺利接受到啦。

 

四、服务端MM的心思(多客户端通信)


在服务端MM收到客户端GG的情书以后,突然发现自己原来还是蛮受欢迎的呢。但是有句俗话说的好,那就是“不能为了一棵树放弃一片森林”。所以服务端MM就想,能不能多接受几个客户端GG的情书呢?(真TMD贱。。。)

OK,既然服务端MM有了这个需求(虽然是很贱的需求),那我们就要来满足她!

来看服务端的代码:
=========================================
==Jserver3.java
=========================================

 

import java.net.*;
import java.io.*;
import java.util.*;

public class Jserver3 {
private ServerSocket server;
List sManager = new ArrayList();

public Jserver3(){}
void startServer() //运行服务器
{
try
{
server=new ServerSocket(719);
System.out.println("服务器套接字已创建成功!");
while(true)
{
Socket socket=server.accept();
System.out.println("已经与客户端连接");
new J_Thread(socket).start();
sManager.add(socket);
System.out.println("当前客户端连结数:"+sManager.size());
}
}catch(Exception e){}finally
{
try
{
server.close();
}catch(Exception e){}
}
}
public static void main(String[] args) {

Jserver3 server=new Jserver3();
server.startServer();
}
class J_Thread extends Thread  //与客户端进行通信的线程类
{
Socket socket;   //套接字引用变量
private DataInputStream reader;   //套接字输入流
private DataOutputStream writer;  //套接字输出流
J_Thread(Socket socket)   //构造函数
{
this.socket=socket;
}
public void run()
{
try
{

reader=new DataInputStream(socket.getInputStream());//获取套接字的输入流
writer=new DataOutputStream(socket.getOutputStream());//获取套接字的输出流
String msg;
while((msg=reader.readUTF())!=null)//如果收到客户端发来的数据
{
//向客户端发送信息
writer.writeUTF("您的情书已经收到");
writer.flush();
System.out.println("来自客户端:"+msg);

}
}catch(Exception e){}finally
{
try
{
sManager.remove(socket);  //删除套接字
//关闭输入输出流及套接字
if(reader!=null)reader.close();
if(writer!=null)writer.close();
if(socket!=null)socket.close();
reader=null;
writer=null;
socket=null;

System.out.println("客户端离开");//向屏幕输出相关信息
System.out.println("当前客户端的连接数:"+sManager.size());
}catch(Exception e){}
}
}
}

}

嘎嘎 在这段代码里,服务端MM为每一个连接的客户端GG分配一个单独的线程,而每一个线程里都持有对应的客户端GG的Socket对象。SO,通过这些多线程,服务端MM就练就了一心N用的功力,可以同时接受N个客户端GG发来的情书了( ,真的太贱了。。。。)

客户端的代码和上面的客户端代码一模一样的,这里就不多说啦!

 


好了,在这里就简单的给大家介绍了如何实现java和flash(flex)实现socket通信的简单功能。有时间的话,再给大家来个简单的聊天室实现(那个时候的服务端MM就更贱了,不仅收着N多客户端GG的情书,竟然还把情书广播给所有的客户端GG,BS!)。嘎嘎 其实原理就是上面所说的这些,大家自己都可以尝试下。

posted @ 2010-09-19 12:33 Eric_jiang 阅读(1953) | 评论 (1)编辑 收藏

Iterator模式定义:
提供一个方法顺序访问一个聚合对象的各个元素,而又不暴露该对象的内部表示。
这个模式在java的类库中已经实现了,在java中所有的集合类都实现了Conllection接口,而Conllection接口又继承了Iterable接口,该接口有一个iterator方法,也就是所以的集合类都可以通过这个iterator方法来转换成Iterator类,用Iterator对象中的hasnext方法来判断是否还有下个元素,next方法来顺序获取集合类中的对象。
下面是Iterator模式的结构图:

 在这个结构图中的Aggregate抽象类也可以定义成接口。下面给出一个例子来说明Iterator模式的使用。
import java.util.ArrayList;

interface Iterator<E>{
 public void first();
 public void next();
 public E currentItem();
 public boolean isDone();
}
class ConcreteIterator<E> implements Iterator<E>{

 private ConcreteAggregate<E> agg;
 private int index=0;
 private int size=0;

    public ConcreteIterator( ConcreteAggregate<E> aggregate) {
  this.agg=aggregate;
  this.index=0;
  this.size=aggregate.size();
 }

 public E currentItem() {
  return agg.getElement(index);
 }

 public void first() {
  index=0;
 }

 public boolean isDone() {
  if(index>=size){
   return true;
  }
  return false;
 }

 public void next() {
  if(index<size){
   index++;
     }
 
    }
}
abstract class Aggregate<E>{
 
 protected abstract Iterator createIterator();
}
class ConcreteAggregate<E> extends Aggregate<E>{
 private ArrayList<E> arrayList=new ArrayList<E>();
 public Iterator createIterator() {
 
  return new ConcreteIterator<E>(this);
 }
 public void add(E o){
  arrayList.add(o);
 }
 public E getElement(int index) {

       if (index<arrayList.size()) {

         return arrayList.get(index);

       } else {

         return null;

       }

    }

    public int size(){

       return arrayList.size();

    }


}

public class Client {

 public static void main(String[] args) {
  ConcreteAggregate<String> aggregate=new ConcreteAggregate<String>();
  aggregate.add("A");
  aggregate.add("B");
  aggregate.add("C");
  aggregate.add("D");
  aggregate.add("E");
  Iterator iterator=aggregate.createIterator();
  for(iterator.first();!iterator.isDone();iterator.next()){
   System.out.println(iterator.currentItem());
  }
 
 }
}
输出结果:
A
B
C
D
E
在这个例子中,我们按照Iterator模式的结构图来创建的例子,我们创建的集合ConcreteAggregate可以存放任何类型的数据,然后使用createIterator方法转换成Iterator对象,使用Iterator对象来按顺序显示集合中的内容。这个模式在Java的jdk中是这样实现的,所以的集合类都实现了Iterable接口,这个接口中有一个iterator方法可以把集合类的对象转换成Iterator类的对象。明白这个原理可以更好的理解Java中的集合类和迭代器,根据这个可以创建功能更加强大的集合类。
小结:Iterator模式主要用在当一个集合类中的元素经常变动时,而不需要改变客户端的代码。


 

posted @ 2010-09-09 16:44 Eric_jiang 阅读(230) | 评论 (0)编辑 收藏

一、 模式定义:
在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存前的状态。
二、 模式解说
在程序运行过程中,某些对象的状态处在转换过程中,可能由于某种原因需要保存此时对象的状态,以便程序运行到某个特定阶段,需要恢复到对象之前处于某个点时的状态。如果使用一些公有接口让其它对象来得到对象的状态,便会暴露对象的实现细节。

三、 结构图
 
1) 备忘录(Memento)角色:备忘录角色存储“备忘发起角色”的内部状态。“备忘发起角色”根据需要决定备忘录角色存储“备忘发起角色”的哪些内部状态。为了防止“备忘发起角色”以外的其他对象访问备忘录。备忘录实际上有两个接口,“备忘录管理者角色”只能看到备忘录提供的窄接口——对于备忘录角色中存放的属性是不可见的。“备忘发起角色”则能够看到一个宽接口——能够得到自己放入备忘录角色中属性。
2) 备忘发起(Originator)角色:“备忘发起角色”创建一个备忘录,用以记录当前时刻它的内部状态。在需要时使用备忘录恢复内部状态。
3) 备忘录管理者(Caretaker)角色:负责保存好备忘录。不能对备忘录的内容进行操作或检查。

class WindowsSystem{

 private String state;
 
 public Memento createMemento(){ //创建系统备份
      return new Memento(state);
 }
 
 public void restoreMemento(Memento m){ //恢复系统
      this.state=m.getState();
 }
 
 public String getState() {
  return state;
 }
 
 public void setState(String state) {
  this.state = state;
  System.out.println("当前系统处于"+this.state);
 }

}

class Memento{
 private String state;

 public Memento(String state) {
  this.state = state;
 }

 public String getState() {
  return state;
 }
 
 public void setState(String state) {
  this.state = state;
 }
}

class User{

 private Memento memento;

 public Memento retrieveMemento() {  //恢复系统
  return this.memento;
 }

 public void saveMemento(Memento memento){  //保存系统
  this.memento = memento;
 }
 
}

public class Test{

 public static void main(String[] args) {
 
   WindowsSystem Winxp = new WindowsSystem(); //Winxp系统
   User user = new User();   //某一用户
   Winxp.setState("好的状态");   //Winxp处于好的运行状态
   user.saveMemento(Winxp.createMemento()); //用户对系统进行备份,Winxp系统要产生备份文件
   Winxp.setState("坏的状态");   //Winxp处于不好的运行状态
   Winxp.restoreMemento(user.retrieveMemento());   //用户发恢复命令,系统进行恢复
   System.out.println("当前系统处于"+Winxp.getState());
  }

}

 

在本例中,WindowsSystem是发起人角色(Orignation),Memento是备忘录角色(Memento),User是备忘录管理角色(Caretaker)。Memento提供了两个接口(注意这里的接口,并不是java中的接口,它指的是可被外界调用的方法):一个是为WindowsSystem 类的宽接口,能够得到WindowsSystem放入Memento的state属性,代码见WindowsSystem的createMemento方法和restoreMemento方法,createMemento方法向Memento放入state属性,restoreMemento方法获得放入的state属性。另一个是为User类提供的窄接口,只能管理Memento而不能对它的内容进行任何操作(见User类)。
五、 优缺点
1) 保持封装边界 使用备忘录可以避免暴露一些只应由原发器管理却又必须存储在原发器之外的信息。该模式把可能很复杂的Originator内部信息对其他对象屏蔽起来,从而保持了封装边界。
2) 它简化了原发器 在其他的保持封装性的设计中,Originator负责保持客户请求过的内部状态版本。这就把所有存储管理的重任交给了Originator。让客户管理它们请求的状态将会简化Originator,并且使得客户工作结束时无需通知原发器。
3) 使用备忘录可能代价很高 如果原发器在生成备忘录时必须拷贝并存储大量的信息,或者客户非常频繁地创建备忘录和恢复原发器状态,可能会导致非常大的开销。除非封装和恢复Originator状态的开销不大,否则该模式可能并不合适。
4) 维护备忘录的潜在代价 管理器负责删除它所维护的备忘录。然而,管理器不知道备忘录中有多少个状态。因此当存储备忘录时,一个本来很小的管理器,可能会产生大量的存储开销。
六、 适用性
1)必须保存一个对象在某一个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态。
2)如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。


 

 


 

posted @ 2010-09-09 16:35 Eric_jiang 阅读(275) | 评论 (0)编辑 收藏

中介者模式(Mediator):用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
 
通用类图:
 
        举例:在一个公司里面,有很多部门、员工(我们统称他们互相为Colleague“同事”),为了完成一定的任务,“同事”之间肯定有许多需要互相配合、交流的过程。如果由各个“同事”频繁地到处去与自己有关的“同事”沟通,这样肯定会形成一个多对多的杂乱的联系网络而造成工作效率低下。
 
        此时就需要一位专门的“中介者”给各个“同事”分配任务,以及统一跟进大家的进度并在“同事”之间实时地进行交互,保证“同事”之间必须的沟通交流。很明显我们知道此时的“中介者”担任了沟通“同事”彼此之间的重要角色了,“中介者”使得每个“同事”都变成一对一的联系方式,减轻了每个“同事”的负担,增强工作效率。
 
        大概理清上面简单例子中的意图之后,给出中介者模式适用场景:
1、一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。
2、一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
3、想定制一个分布在多个类中的行为,而又不想生成太多的子类。
 
        其实,中介者模式又被称为“调停者”模式,我们可以理解为一群小孩子(同事)吵架了,这时就很需要一位大人(调停者)过来处理好小孩子们的关系,以免发生不必要的冲突。“中介者”和“调停者”只不过是同一个英语单词“Mediator”的不同翻译罢了。反正最最重要的是:中介者就是一个处于众多对象,并恰当地处理众多对象之间相互之间的联系的角色。
 
        下面给出具体的代码例子,对比通用类图增加了AbstractColleague抽象同事类和AbstractMediator抽象中介者,另外就是两个具体同事类和一个具体中介者,代码中有较多注释,相应类图也不给出了,应该不难理解的:


同事类族:
 //抽象同事类 
 abstract class AbstractColleague {   
  protected AbstractMediator mediator;         
  /**既然有中介者,那么每个具体同事必然要与中介者有联系,
  * 否则就没必要存在于 这个系统当中,这里的构造函数相当
  * 于向该系统中注册一个中介者,以取得联系      
  */   
  public AbstractColleague(AbstractMediator mediator) {
   this.mediator = mediator;     
  }
  // 在抽象同事类中添加用于与中介者取得联系(即注册)的方法
  public void setMediator(AbstractMediator mediator) {
   this.mediator = mediator;
  }

 }
 
  //具体同事A 
  class ColleagueA extends AbstractColleague {
  
   //每个具体同事都通过父类构造函数与中介者取得联系
   public ColleagueA(AbstractMediator mediator) {super(mediator);} 
  
   //每个具体同事必然有自己分内的事,没必要与外界相关联
   public void self(){
  System.out.println("同事A --> 做好自己分内的事情 ...");
   }
  
   //每个具体同事总有需要与外界交互的操作,通过中介者来处理这些逻辑并安排工作
   public void out() {
    System.out.println("同事A --> 请求同事B做好分内工作 ...");
    super.mediator.execute("ColleagueB", "self");
   }
  
  }
 
  //具体同事B 
  class ColleagueB extends AbstractColleague {    
 
   public ColleagueB(AbstractMediator mediator) {  
  super(mediator);    
   }   
  
   public void self() {    
  System.out.println("同事B --> 做好自己分内的事情 ..."); 
   } 
  
   public void out() {   
  System.out.println("同事B --> 请求同事A做好分内工作  ...");    
  super.mediator.execute("ColleagueA", "self"); 
   }
  }
//抽象中介者 
 abstract class AbstractMediator {          
   //中介者肯定需要保持有若干同事的联系方式     
   protected Hashtable<String, AbstractColleague> colleagues = new Hashtable<String, AbstractColleague>();           
   //中介者可以动态地与某个同事建立联系     
   public void addColleague(String name, AbstractColleague c) {         
   this.colleagues.put(name, c);     
   }             
   //中介者也可以动态地撤销与某个同事的联系    
   public void deleteColleague(String name) {
   this.colleagues.remove(name);    
   }           
   //中介者必须具备在同事之间处理逻辑、分配任务、促进交流的操作    
   public abstract void execute(String name, String method);
  } 
 //具体中介者
 class Mediator extends AbstractMediator{
  //中介者最重要的功能,来回奔波与各个同事之间
  public void execute(String name, String method) {
   if("self".equals(method)){ 
    //各自做好分内事
    if("ColleagueA".equals(name)) {     
     ColleagueA colleague = (ColleagueA)super.colleagues.get("ColleagueA");         
     colleague.self();          
    }else {               
     ColleagueB colleague = (ColleagueB)super.colleagues.get("ColleagueB");          
     colleague.self();        
    } 
    
   }else { //与其他同事合作       
     if("ColleagueA".equals(name)) {    
      ColleagueA colleague = (ColleagueA)super.colleagues.get("ColleagueA");           
      colleague.out();       
     }else {           
      ColleagueB colleague = (ColleagueB)super.colleagues.get("ColleagueB");        
      colleague.out();       
     }    
   } 
  }
 }

 //测试类
 public class Client {    
  public static void main(String[] args) {    

  //创建一个中介者       
  AbstractMediator mediator = new Mediator();      

  //创建两个同事      
  ColleagueA colleagueA = new ColleagueA(mediator); 
  ColleagueB colleagueB = new ColleagueB(mediator);       

  //中介者分别与每个同事建立联系   
  mediator.addColleague("ColleagueA", colleagueA);      
  mediator.addColleague("ColleagueB", colleagueB);   
  
  //同事们开始工作        
  colleagueA.self();       
  colleagueA.out();   
  System.out.println("======================合作愉快,任务完成!\n");  
  
  colleagueB.self();      
  colleagueB.out();       
  System.out.println("======================合作愉快,任务完成!"); 
  }
 }
 测试结果:
 同事A --> 做好自己分内的事情 ... 
 同事A --> 请求同事B做好分内工作 ... 
 同事B --> 做好自己分内的事情 ... 
 ======================合作愉快,任务完成!
 同事B --> 做好自己分内的事情 ... 
 同事B --> 请求同事A做好分内工作  ...
 同事A --> 做好自己分内的事情 ...
 ======================合作愉快,任务完成! 

 虽然以上代码中只有两个具体同事类,并且测试类中也只是创建了两个同事,但是这些我们都可以根据中介者模式的宗旨进行适当地扩展,即增加具体同事类,然后中介者就得担负更加重的任务了。为啥?我们看到上面具体中介者类Mediator中的execute()方法中现在就有一堆冗长的判断代码了。虽然可以把它分解并增加到Mediator类中的其它private方法中,但是具体的业务逻辑是少不了的。
 
所以,在解耦同事类之间的联系的同时,中介者自身也不免任务过重,因为几乎所有的业务逻辑都交代到中介者身上了,可谓是“万众期待”的一个角色了。这就是中介者模式的不足之处了 。
此外,上面这个代码例子是相当理想的了,有时候我们根本抽取不了“同事”之间的共性来形成一个AbstractColleague抽象同事类,这也大大增加了中介者模式的使用难度。  

由于上面代码实现中存在"双向关联暴露在App中"的不足之处,修改上面代码,如下:

 

修改后的同事类族:

 //抽象同事类
 abstract class AbstractColleague {    
 
  protected AbstractMediator mediator;   
  
  //舍去在构造函数中建立起与中介者的联系
  //public AbstractColleague(AbstractMediator mediator) {
  //      this.mediator = mediator; 
  //} 
  
  // 在抽象同事类中添加用于与中介者取得联系(即注册)的方法  
  public void setMediator(AbstractMediator mediator) {   
   this.mediator = mediator;   
  }
 }
 
 //具体同事A
 
  class ColleagueA extends AbstractColleague {
 
   //舍去在构造函数中建立起与中介者的联系
   // public ColleagueA(AbstractMediator mediator) {
   //  super(mediator);
   // } 
  
   //每个具体同事必然有自己分内的事,没必要与外界相关联  
   public void self() {    
   System.out.println("同事A --> 做好自己分内的事情 ...");  
   }
  
   //每个具体同事总有需要与外界交互的操作,通过中介者来处理这些逻辑并安排工作     
   public void out() {      
   System.out.println("同事A --> 请求同事B做好分内工作 ...");    
   super.mediator.execute("ColleagueB", "self"); 
   }
 
  }  
 
 //具体同事B 
 
 class ColleagueB extends AbstractColleague {  

  //舍去在构造函数中建立起与中介者的联系 
  // public ColleagueB(AbstractMediator mediator) {
  //      super(mediator); 
  // }     
  
  public void self() {      
   System.out.println("同事B --> 做好自己分内的事情 ...");  
  } 
  
  public void out() {   
   System.out.println("同事B --> 请求同事A做好分内工作  ...");    
   super.mediator.execute("ColleagueA", "self");   
  }
 }

 

  修改后的中介者:

  //抽象中介者
  abstract class AbstractMediator {   
  //中介者肯定需要保持有若干同事的联系方式   

  protected Hashtable<String, AbstractColleague> colleagues = new Hashtable<String, AbstractColleague>();   
  
  //中介者可以动态地与某个同事建立联系   
  public void addColleague(String name, AbstractColleague c) { 
   // 在中介者这里帮助具体同事建立起于中介者的联系   
   c.setMediator(this);   
   this.colleagues.put(name, c); 
  } 
  
  //中介者也可以动态地撤销与某个同事的联系   
  public void deleteColleague(String name) { 
   this.colleagues.remove(name);  
  } 
       
  //中介者必须具备在同事之间处理逻辑、分配任务、促进交流的操作 
  public abstract void execute(String name, String method); 
  }
  
  //测试类
  public class Client { 
   
   public static void main(String[] args) { 
   
    //创建一个中介者    
    AbstractMediator mediator = new Mediator();   
    
    //不用构造函数为具体同事注册中介者来取得联系了
    // ColleagueA colleagueA = new ColleagueA(mediator);
    // ColleagueB colleagueB = new ColleagueB(mediator);  
    
    ColleagueA colleagueA = new ColleagueA(); 
    ColleagueB colleagueB = new ColleagueB(); 
    
    //中介者分别与每个同事建立联系 
    mediator.addColleague("ColleagueA", colleagueA); 
    mediator.addColleague("ColleagueB", colleagueB);  
    
    //同事们开始工作      
    colleagueA.self();     
    colleagueA.out();   
    System.out.println("======================合作愉快,任务完成!\n");  
    
    colleagueB.self();       
    colleagueB.out();      
    System.out.println("======================合作愉快,任务完成!");  
    
   }
  }   测试之后的结果与修改前一样。



posted @ 2010-09-09 15:56 Eric_jiang 阅读(2792) | 评论 (1)编辑 收藏

Interpreter模式也叫解释器模式,是由GoF提出的23种设计模式中的一种。Interpreter是行为模式之一,它是一种特殊的设计模式,它建立一个解释器,对于特定的计算机程序设计语言,用来解释预先定义的文法。
本文介绍设计模式中的解释器(Interpreter)模式的概念,用法,以及实际应用中怎么样使用Interpreter模式进行开发。

Interpreter模式的概念

Interpreter是一种特殊的设计模式,它建立一个解释器,对于特定的计算机程序设计语言,用来解释预先定义的文法。简单地说,Interpreter模式是一种简单的语法解释器构架。

Interpreter模式有很多种实现方法,下面我们给出Interpreter模式的一种类图来说明Interpreter模式:

 

在上图中,我们假设需要在Client中解释某文法,Client调用Context来存储文法规则,并调用解释器AbstractionExpression类树来对该文法加以解释。注意,上图只是Interpreter模式的一种实现方式的类图。


Context
    解释器上下文环境类。用来存储解释器的上下文环境,比如需要解释的文法等。
AbstractExpression
    解释器抽象类。
ConcreteExpression
    解释器具体实现类。

 


Interpreter模式的实现范例
为了帮助大家理解Interpreter模式的基本概念,我们在这里只举一个最简单的例子。
让一个表达式a经过PlusExpression解释器处理后使该表达式+1,经过MinusExpression解释器处理后使该表达式-1。


代码:

import java.util.ArrayList;
import java.util.List;

public class Client {
    public static void main(String []args) {
        String inputExpr = "10";
       
        Context context = new Context(inputExpr);
       
        List list = new ArrayList();

        list.add(new PlusExpression());
        list.add(new PlusExpression());
        list.add(new MinusExpression());
        list.add(new MinusExpression());
        list.add(new MinusExpression());

        for (int i=0;i<list.size();i++) {
          AbstractExpression expression = (AbstractExpression)list.get(i);
          expression.interpret(context);
        }
       
        System.out.println(context.getOutput());
    }
}


/**
* Context
*
*/
class Context {
    private String input;
    private int output;

    public Context (String input) {
        this. input = input;
    }

    public String getInput() {
        return input;
    }

    public void setInput(String input) {
        this.input = input;
    }

    public int getOutput() {
        return output;
    }

    public void setOutput(int output) {
        this.output = output;
    }
}


/**
* Expression & subclass
*
*/
abstract class AbstractExpression {
    public abstract void interpret(Context context);
}

class PlusExpression extends AbstractExpression {
    public void interpret(Context context) {
        System.out.println("PlusExpression ++");
        String input = context.getInput();
        int parsedResult = Integer.parseInt(input);
        parsedResult ++;
        context.setInput(String.valueOf(parsedResult));
        context.setOutput(parsedResult);
    }
}

class MinusExpression extends AbstractExpression {
    public void interpret(Context context) {
        System.out.println("PlusExpression --");
        String input = context.getInput();
        int parsedResult = Integer.parseInt(input);
        parsedResult --;
        context.setInput(String.valueOf(parsedResult));
        context.setOutput(parsedResult);
    }
}

 

运行并显示Client:

C:\Interpreter>javac *.java
C:\Interpreter>java Client
PlusExpression ++
PlusExpression ++
PlusExpression --
PlusExpression --
PlusExpression --
9
C:\Interpreter>

posted @ 2010-09-09 15:40 Eric_jiang 阅读(2965) | 评论 (0)编辑 收藏

1.这一节我们的任务是创建一个类似智能家居的万能遥控器,控制各种家电。我们需要将“请求”封装成对象(一个命令对象通过在特定接收者上绑定一组动作来封装请求),以便使用不同的请求、队列、或者日志来参数化其对象——这就是命令模式。

2.我们具体来看一个例子:

首先我们要完成对命令的对象封装:

public interface Command {
    public void execute();
}

只有一个方法,所有的具体命令的对象都要实现这个接口,这就做到了封装,比如对于灯这个对象,

public class Light {

    public Light() {
    }

    public void on() {
        System.out.println("Light is on");
    }

    public void off() {
        System.out.println("Light is off");
    }
}

我们可以通过上述接口封装“开灯”这个命令,这个就是所谓的命令对象,它把动作和接收者包进对象之中,只暴露一个execute方法:

public class LightOnCommand implements Command {
    Light light;
    public LightOnCommand(Light light) {
        this.light = light;
    }
    public void execute() {
        light.on();
    }
}

而我们的遥控器对于上述封装要一无所知,这样才能做到解耦:

public class SimpleRemoteControl {
    Command slot;
    public SimpleRemoteControl() {}
    public void setCommand(Command command) {
        slot = command;
    }
    public void buttonWasPressed() {
        slot.execute();
    }
}

我们现在试着用一下这个遥控器,我们首先创建一个遥控器,然后创建开灯这个命令并置于其中,最后按下按键,这样,遥控器不知道是哪个对象(实际上是灯)进行了哪个动作(实际上是开灯这个动作)就可以完成请求的发出。如此一来,遥控器和灯之间的耦合性就非常低了:

public class RemoteControlTest {
    public static void main(String[] args) {
        SimpleRemoteControl remote = new SimpleRemoteControl();
        Light light = new Light();
        LightOnCommand lightOn = new LightOnCommand(light);
        remote.setCommand(lightOn);
        remote.buttonWasPressed();
        }
}

很简单的,我们想要在一个遥控机中实现控制多个家电的能力就可以用一个数组来维护这样的一组命令,可以看做提供了多个命令的插槽:

public class RemoteControl {
    Command[] onCommands;
    Command[] offCommands;
    public RemoteControl() {
        onCommands = new Command[7];
        offCommands = new Command[7];
        Command noCommand = new NoCommand();
        for (int i = 0; i < 7; i++) {
            onCommands[i] = noCommand;
            offCommands[i] = noCommand;
        }
    }
    public void setCommand(int slot, Command onCommand, Command offCommand) {
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }
    public void onButtonWasPushed(int slot) {
        onCommands[slot].execute();
    }
    public void offButtonWasPushed(int slot) {
        offCommands[slot].execute();
    }
    public String toString() {
    }
}

你可能会注意到一个叫做noCommand的对象出现在初始化遥控器对象时。这个是个小技巧,我们并不想每次都检查某个插槽是不是都绑定了命令,那么我们就引入了这个东东,实现一个不做事情的命令:

public class NoCommand implements Command {
    public void execute() { }
}

这样初始化就让每一个插槽都有命令了,以后若不进一步指明命令的插槽,那么就是默认这个noCommand对象。这就是一个典型的“空对象”例子,当你不想返回一个有意义的对象时,空对象就十分有用,此时空对象可以做成判断NULL或者提示“未绑定”信息的工作。

3.我们在命令模式中也可以设置类似undo的撤销命令来撤销发出的命令请求:

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

我们还是拿开电灯这个操作来说明,毕竟这足够简单:对于一个开灯命令,其对应的撤销命令自然是“关电灯”:

public class LightOnCommand implements Command {
    Light light;
    public LightOnCommand(Light light) {
        this.light = light;
    }
    public void execute() {
        light.on();
    }
    public void undo() {
        light.off();
    }
}

虽然这简单之极,这还没完,我们必须让遥控器记住上次到底做了什么操作,才可能去撤销:

package headfirst.command.undo;

import java.util.*;

//
// This is the invoker
//
public class RemoteControlWithUndo {
    Command[] onCommands;
    Command[] offCommands;
    Command undoCommand;
    public RemoteControlWithUndo() {
        onCommands = new Command[7];
        offCommands = new Command[7];
        Command noCommand = new NoCommand();
        for(int i=0;i<7;i++) {
            onCommands[i] = noCommand;
            offCommands[i] = noCommand;
        }
        undoCommand = noCommand;
    }
    public void setCommand(int slot, Command onCommand, Command offCommand) {
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }
    public void onButtonWasPushed(int slot) {
        onCommands[slot].execute();
        undoCommand = onCommands[slot];//记录操作动作
    }
    public void offButtonWasPushed(int slot) {
        offCommands[slot].execute();
        undoCommand = offCommands[slot];//记录操作动作
    }
    public void undoButtonWasPushed() {
        undoCommand.undo();//发出撤销命令
    }
    public String toString() {
    }
}

对于电灯,我们只有两种状态,但是要是多个状态呢?我们举一个吊扇的例子,吊扇有多个转速,高中低关,四个状态:

public class CeilingFan {
    String location = "";
    int level;
    public static final int HIGH = 2;
    public static final int MEDIUM = 1;
    public static final int LOW = 0;
    public CeilingFan(String location) {
        this.location = location;
    }
    public void high() {
        // turns the ceiling fan on to high
        level = HIGH;
        System.out.println(location + " ceiling fan is on high");
    }

    public void medium() {
        // turns the ceiling fan on to medium
        level = MEDIUM;
        System.out.println(location + " ceiling fan is on medium");
    }

    public void low() {
        // turns the ceiling fan on to low
        level = LOW;
        System.out.println(location + " ceiling fan is on low");
    }
    public void off() {
        // turns the ceiling fan off
        level = 0;
        System.out.println(location + " ceiling fan is off");
    }
    public int getSpeed() {
        return level;
    }
}

那么在实现风扇各个转速命令时,就要去记录在执行这个命令前风扇的转速,其撤销命令中则根据记录的部分完成undo操作。

public class CeilingFanHighCommand implements Command {
    CeilingFan ceilingFan;
    int prevSpeed;

    public CeilingFanHighCommand(CeilingFan ceilingFan) {
        this.ceilingFan = ceilingFan;
    }
    public void execute() {
        prevSpeed = ceilingFan.getSpeed();
        ceilingFan.high();
    }
    public void undo() {
        switch (prevSpeed) {
            case CeilingFan.HIGH:     ceilingFan.high(); break;
            case CeilingFan.MEDIUM: ceilingFan.medium(); break;
            case CeilingFan.LOW:     ceilingFan.low(); break;
            default:                 ceilingFan.off(); break;
        }
    }
}

当然你也可以实现诸如多个命令批量执行和完成多个撤销操作。

命令模式可以用于工作队列和日志操作等方面。

 

 

posted @ 2010-09-09 15:29 Eric_jiang 阅读(260) | 评论 (0)编辑 收藏

 

 显然,击鼓传花符合责任链模式的定义。参加游戏的人是一个个的具体处理者对象,击鼓的人便是客户端对象。花代表酒令,是传向处理者的请求,每一个参加游戏的人在接到传来的花时,可选择的行为只有两个:一是将花向下传;一是执行酒令---喝酒。一个人不能既执行酒令,又向下家传花;当某一个人执行了酒令之后,游戏重新开始。击鼓的人并不知道最终是由哪一个做游戏的人执行酒令,当然执行酒令的人必然是做游戏的人们中的一个。

  击鼓传花的类图结构如下:

 

图5、击鼓传花系统的类图定义。

  单独考虑击鼓传花系统,那么像贾母、贾赦、贾政、贾宝玉和贾环等传花者均应当是“具体传花者”的对象,而不应当是单独的类;但是责任链模式往往是建立在现有系统的基础之上的,因此链的结构和组成不由责任链模式本身决定。


  系统的分析
  在《红楼梦》第七十五回里生动地描述了贾府里的一场击鼓传花游戏:“贾母坐下,左垂首贾赦,贾珍,贾琏,贾蓉,右垂首贾政,宝玉,贾环,贾兰,团团围坐。...贾母便命折一枝桂花来,命一媳妇在屏后击鼓传花。若花到谁手中,饮酒一杯...于是先从贾母起,次贾赦,一一接过。鼓声两转,恰恰在贾政手中住了,只得饮了酒。”这场游戏接着又把花传到了宝玉和贾赦手里,接着又传到了在贾环手里...


  如果用一个对象系统描述贾府,那么贾母、贾赦、贾政、贾宝玉和贾环等等就应当分别由一个个具体类代表,而这场击鼓传花游戏的类图,按照责任链模式,应当如下图所示:

 

 图6、红楼梦中的击鼓传花游戏的示意性类图。
 换言之,在击鼓传花游戏里面,有下面的几种角色:
 抽象传花者,或Handler角色、定义出参加游戏的传花人要遵守的规则,也就是一个处理请求的接口 和对下家的引用;
 具体传花者,或ConcreteHandler角色、每一个传花者都知道下家是谁,要么执行酒令,要么把花 向下传。这个角色由贾母、贾赦、贾珍、贾琏、贾蓉、贾政、宝玉、贾环、贾兰等扮演。 击鼓人,或Client角色、即行酒令的击鼓之人。《红楼梦》没有给出此人的具体姓名,只是说由“一 媳妇”扮演。


图7、贾府这次击鼓传花的示意性对象图。

  可以看出,击鼓传花游戏满足责任链模式的定义,是纯的责任链模式的例子.
Java系统的解下面的类图给出了这些类的具体接口设计。读者不难看出,DrumBeater(击鼓者)、Player(传花者)、JiaMu(贾母)、JiaShe(贾赦)、JiaZheng(贾政)、JiaBaoYu(宝玉)、JiaHuan(贾环)等组成这个系统。



图8、击鼓传花的类图完全符合责任链模式的定义。

  下面是客户端类DrumBeater的源代码:

 //DrumBeater的源代码
 public class DrumBeater
 {
   private static Player player;
   static public void main(String[] args)
    {
     player = new JiaMu( new JiaShe( new JiaZheng( new JiaBaoYu(new JiaHuan(null)))));
     player.handle(4);
    }
 } 

 //抽象传花者Play类的源代码
 abstract class Player
 {
  abstract public void handle(int i);
  private  Player successor;
  public   Player() { successor = null;}

  protected void setSuccessor(Player aSuccessor)
  {
    successor = aSuccessor;
  }

  public void next(int index)
  {
    if( successor != null )
     {
      successor.handle(index);
     }
    else
     {
      System.out.println("Program terminated.");
     }
    }
  } 
 }
 

  抽象类Player给出了两个方法的实现,以格式setSuccessor(),另一个是next()。前者用来设置一个传花者对象的下家,后者用来将酒令传给下家。Player类给出了一个抽象方法handle(),代表执行酒令。

  下面的这些具体传花者类将给出handle()方法的实现。

 //贾母的JiaMu类
 class JiaMu extends Player
 {
    public JiaMu(Player aSuccessor)
    {
     this.setSuccessor(aSuccessor);
    }
    public void handle(int i)
    {
     if( i == 1 )
     {
      System.out.println("Jia Mu gotta drink!");
     }
     else
     {
    System.out.println("Jia Mu passed!");
    next(i);
     }
    }
 } 

 
   //贾赦的JiaShe类
   class JiaShe extends Player
  {
   public JiaShe(Player aSuccessor)
   {
    this.setSuccessor(aSuccessor);
   }
   public void handle(int i)
    {
     if( i == 2 )
     {
      System.out.println("Jia She gotta drink!");
     }
     else
     {
      System.out.println("Jia She passed!");
      next(i);
     }
    }
  } 

  
   //贾政的JiaZheng类
   class JiaZheng extends Player
  {
   public JiaZheng(Player aSuccessor)
   {
    this.setSuccessor(aSuccessor);
   }
   public void handle(int i)
   {
    if( i == 3 )
    {
     System.out.println("Jia Zheng gotta drink!");
    }
    else
    {
     System.out.println("Jia Zheng passed!");
     next(i);
    }
   }
  } 

 
 //贾宝玉的JiaBaoYu类
   class JiaBaoYu extends Player
  {
   public JiaBaoYu(Player aSuccessor)
   {
    this.setSuccessor(aSuccessor);
   }
   public void handle(int i)
   {
    if( i == 4 )
    {
     System.out.println("Jia Bao Yu gotta drink!");
    }
    else
    {
     System.out.println("Jia Bao Yu passed!");
     next(i);
    }
   }
  } 

  
 //JiaHuan类
 class JiaHuan extends Player
  {
   public JiaHuan(Player aSuccessor)
    {
     this.setSuccessor(aSuccessor);
    }
   public void handle(int i)
   {
    if( i == 5 )
    {
     System.out.println("Jia Huan gotta drink!");
    }
    else
    {
     System.out.println("Jia Huan passed!");
     next(i);
    }
   }
  } 

 

  可以看出,DrumBeater设定了责任链的成员和他们的顺序:责任链由贾母开始到贾环,周而复始。JiaMu类、JiaShe类、JiaZheng类、JiaBaoYu类与JiaHuan类均是抽象传花者Player类的子类。

  本节所实现的DrumBeater类在把请求传给贾母时,实际上指定了由4号传花者处理酒令。虽然DrumBeater并不知道哪一个传花者类持有号码4,但是这个号码在本系统一开始就写死的。这当然并不符合击鼓传花游戏的精神,因为这个游戏实际上要求有两个同时进行的过程:击鼓过程和传花过程。击鼓应当是定时停止的,当击鼓停止时,执行酒令者就确定了。但是本节这样做可以使问题得到简化并将读者的精力放在责任链模式上,而不是两个过程的处理上。
 
在什么情况下使用责任链模式

  在下面的情况下使用责任链模式:

  第一、系统已经有一个由处理者对象组成的链。这个链可能由复合模式给出,

  第一、当有多于一个的处理者对象会处理一个请求,而且在事先并不知道到底由哪一个处理者对象处理一个请求。这个处理者对象是动态确定的。

  第二、当系统想发出一个请求给多个处理者对象中的某一个,但是不明显指定是哪一个处理者对象会处理此请求。

  第三、当处理一个请求的处理者对象集合需要动态地指定时。

  使用责任链模式的长处和短处

  责任链模式减低了发出命令的对象和处理命令的对象之间的耦合,它允许多与一个的处理者对象根据自己的逻辑来决定哪一个处理者最终处理这个命令。换言之,发出命令的对象只是把命令传给链结构的起始者,而不需要知道到底是链上的哪一个节点处理了这个命令。

  显然,这意味着在处理命令上,允许系统有更多的灵活性。哪一个对象最终处理一个命令可以因为由那些对象参加责任链、以及这些对象在责任链上的位置不同而有所不同。

  责任链模式的实现

  链结构的由来

  值得指出的是,责任链模式并不创建出责任链。责任链的创建必须有系统的其它部分完成。

  责任链模式减低了请求的发送端和接收端之间的耦合,使多个对象都有机会处理这个请求。一个链可以是一条线,一个树,也可以是一个环。链的拓扑结构可以是单连通的或多连通的,责任链模式并不指定责任链的拓扑结构。但是责任链模式要求在同一个时间里,命令只可以被传给一个下家(或被处理掉);而不可以传给多于一个下家。在下面的图中,责任链是一个树结构的一部分。

 

图9、责任链是系统已有的树结构的一部分。图中有阴影的对象给出了一个可能的命令传播路径。

  责任链的成员往往是一个更大的结构的一部分。比如在前面所讨论的《红楼梦》中击鼓传花的游戏中,所有的成员都是贾府的成员。如果责任链的成员不存在,那么为了使用责任链模式,就必须创建它们;责任链的具体处理者对象可以是同一个具体处理者类的实例。

posted @ 2010-09-09 14:44 Eric_jiang 阅读(1214) | 评论 (0)编辑 收藏

仅列出标题
共57页: First 上一页 42 43 44 45 46 47 48 49 50 下一页 Last