您的位置:首页 > 编程语言 > Java开发

学习:Java设计模式—Comman:1

2013-03-10 21:31 399 查看
命令模式:

定义:把一个请求或者操作封装在命令对象中。命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。





Invoker类 被客户端调用,可以接受命令请求,设计命令队列,决定是否相应该请求,记录或撤销或重做命令请求,记录日志等等.

public class Invoker {  

private Command command;  
public void setOrder(Command command) {  

this.command = command;  
    }  
public void ExecuteCommand() {  

        command.ExecuteCommand();  
    }  
}  

Command类,将一个请求封装成一个对象,将一个请求具体化,方便对请求记录。

public abstract
class Command {  
protected Receiver receiver;  

public Command(Receiver receiver){  

this.receiver = receiver;  
    }  
public abstract
void ExecuteCommand();  
}  

ConcreteCommand类,可以将Receiver对象放到这个类里面,这个类具体实现了要怎么处理这个用户的请求。

public class ConcreteCommand
extends Command {  
public ConcreteCommand(Receiver receiver){  

super(receiver);  

    }  
@Override 
public void ExecuteCommand() {  

        receiver.Execute();  
    }  
}  

Receiver类,其实这个类可以没有,不过为了让设计看起来更整洁清楚。

public class Receiver {  

public void Execute(){  

        System.out.println("Receiver excute!");  

    }  
}  

最后一个Client类。

public class Client {  

public static
void main(String[] args) {  
        Receiver r = new Receiver();  

        Command c = new ConcreteCommand(r);   

        Invoker i = new Invoker();  

        i.setOrder(c);  
        i.ExecuteCommand();  
    }  
}  

命令模式在MVC中的应用:

      Struts中,在模型层都要继承一个Action接口,并实现execute方法,其实这个Action就是命令类。为什么Struts会应用命令模 式,是因为Struts的核心控制器ActionServlet只有一个,相当于Invoker,而模型层的类会随着不同的应用有不同的模型类,相当于具 体的Command。这样,就需要在ActionServlet和模型层之间解耦,而命令模式正好解决这个问题。

      MVC Model2 实现的Web框架示意图:





说明:

视图层采用JSP实现
控制器采用Servlet实现,整个框架采用同一个Servlet,以实现请求的中转
模型层采用Java实现,主要决定用来做什么
在模型层后添加了一个DAO,目的是将决定做什么和具体怎么做分开
      整个Web框架大致的流程是:首先客户端发送请求,提交JSP页面给中转器(Servlet);中转器根据客户的请求,选择相应的模型层,即Logic,Logic进行相应的逻辑处理;如果需要使用数据库,则通过DAO进行相应的数据库操作。

下面主要看一下控制层和模型层的设计,应用命令模式:

1.控制层设计

      控制层主要用来转发从视图层传来的数据和请求到相对应的模型层,因此,实现它最好的方式莫过于使用Servlet了。当从视图层获取请求后,首先通过对web.xml文件的配置,使其转入Servlet,在Servlet中完成对页面中数据的封装和对相应模型的选择,然后再到相应的模型层进行数据处理; 当在模型层数据处理完毕后,通过RequestDispatcher将处理后的数据返回相应的视图页面。

      在Servlet中,将使用doPost()来处理相应的中转请求,如果开发人员使用get提交方式,则使用如下方式进行处理。示例代码如下:

public void doGet(HttpServletRequest req, HttpServletResponse res) 

throws ServletException, IOException { 

    doPost(req, res); 

//使用post提交方式 
public void doPost(HttpServletRequest req, HttpServletResponse res) 

throws ServletException, IOException { 

    do_Dispatcher (req, res); 


代码说明:

不论采用get还是post提交方式,都将执行do_Dispatcher(req, res)方法。
do_Dispatcher(req, res)是用来处理视图层发送来的请求的方法。
      如果直接使用request方式来获取从页面提交的数据,在要获取的数据比较多的情况下,会比较烦琐,而且直接将request传递给模型层不符合Model  2规范。所以,这里将对从页面传来的值进行封装,将其放在一个Map中,然后再传递给模型层,这样在模型层就可以直接使用Map中的值。示例代码如下:

private HashMap getRequestToMap(HttpServletRequest req)
throws Exception { 
    req.setCharacterEncoding("GBK"); 

        HashMap infoIn = new HashMap(); 

        for (Enumeration e = req.getParameterNames(); e.hasMoreElements ();) 

                {//获取页面中所有元素名 

            String strName = (String)e.nextElement(); 
            String[] values = (String[]) req.getParameterValues (strName); 

                //根据名称获取对应的值 

            if (values ==
null) {//假如没有值 

                infoIn.put(strName, ""); 

            } else
if (values.length == 1) {//假如只有一个值 

                infoIn.put(strName, values[0]); 

            } else {//假如有多个值 

                infoIn.put(strName, values); 
            } 
        } 
    return infoIn; 



代码说明:

req.setCharacterEncoding("GBK"),这里首先将从视图层传来的数据设定编码为GBK。
HashMap infoIn = new HashMap(),定义一个HashMap,用来存放从request中获取的数据。
req.getParameterNames(),用来获取从页面中传来的所有元素。
req.getParameterValues(),用来根据元素名称来获取元素对应的值,并将元素名称和值的对应关系存入HashMap中。如果元素的值为空,则在HashMap中将元素名称对应的值置为空;如果只有一个值,则将该值存入;如果有多个值,则存入数组。
命令模式使用:

一个视图对应一个模型,也可能一个视图对应多个模型,但只有一个控制器,所以,为了实现一个控制器可以转发到多个模型中去,就需要使用接口,让所有 模型都实现这个接口,然后在控制器里,仅仅是面对接口编程即可。这里定义一个接口Action.java,Action.java的示例代码如下:

//******* Action.java************** 

import java.util.*; 
public interface Action{ 

    public HashMap doAction(HashMap infoIn); 



在控制器中只针对这个接口处理即可。示例代码如下:

Actionaction = (Action) Class.forName(getActionName(systemName, 

logicName)).newInstance(); 
HashMap infoOut = action.doAction(infoIn); 

代码说明:

getActionName()方法是获取实现接口Action的类的名称和所在的包。示例代码如下:

private String getActionName(String systemName ,String actionName) 

throws IOException, Exception { 

return "com. " + systemName +
".action." + actionName; 


使用RequestDispatcher返回视图层。示例代码如下:

req.setAttribute("infoOut", infoOut); 

RequestDispatcher rd = req.getRequestDispatcher("/"+ systemName + 

"/jsp/" + forwardJsp+
".jsp"); 
rd.forward(req, res); 

代码说明:

这里表示JSP文件放在项目中系统名下的jsp文件夹下。
2.模型层设计

假定有一个模型层类为WebExamAction.java,主要用来负责在线考试系统的业务处理,则这个类要实现Action接口。示例代码如下:

//******* WebExamAction.java************** 

import java.util.HashMap; 
public class WebExamAction
implements Action{ 
    //根据页面的请求,进行动作的转换 
    public HashMap doAction(HashMap infoIn) { 

        String action = (infoIn.get("action") ==
null) ? "" : 

(String)infoIn.get("action"); 

        HashMap infoOut = new HashMap(); 

        if (action.equals(""))           infoOut =
this.doInit (infoIn); 
        else if (action.equals("insert")) infoOut =
this.doInsert (infoIn); 
        return infoOut; 

    } 
    /**该方法设置用户登录时页面的初始信息 
    * @param infoIn 
    * @return HashMap 

    */ 
    private HashMap doInit(HashMap infoIn) { 

        HashMap infoOut = infoIn; 
        int clerkId = (infoIn.get("clerkId") ==
null || "".equals 

(infoIn.get("clerkId"))) ? -1 : Integer.parseInt((String) 

infoIn.get ("clerkId")); 

        try { 
            //取得考生姓名 

            Users user = new Users(); 

            String clerkName = user.getName(clerkId);  

            infoOut.put("clerkName", clerkName); 

        } catch(Exception e) { 

            e.printStackTrace(); 
        } finally { 

            return infoOut; 

        } 
    } 
    /**该方法用来进行新增
    * @param infoIn
    * @return HashMap
    */ 
    private HashMap doInsert(HashMap infoIn) { 

        HashMap infoOut = infoIn; 
        int clerkId = (infoIn.get("clerkId") ==
null || "".equals 

(infoIn.get("clerkId"))) ? -1 : Integer.parseInt((String) 

infoIn.get ("clerkId")); 

        try { 
            //取得考生姓名 

            Users user = new Users(); 

            String clerkName = user.getName(clerkId);  

            infoOut.put("clerkName", clerkName); 

        } catch(Exception e) { 

            e.printStackTrace(); 
        } finally { 

            return infoOut; 

        } 
    } 


代码说明:

这里,在doAction中根据从页面传来的action进行动作请求的转换。
通过一个名为infoIn的HashMap,来负责传入页面中元素的值。
通过一个名为infoOut的HashMap,来负责将处理后的数据传出。
可以看出,如果模型层都实现接口Action,实现doAction方法,即可实现动作请求的转换。

命令模式要点:

1.Command模式的根本目的在于将“行为请求者”与“行为实现者”解耦,在面向对象语言中,常见的实现手段是“将行为抽象为对象”。  

2.实现Command接口的具体命令对象ConcreteCommand有时候根据需要可能会保存一些额外的状态信息。   

3.通过使用Compmosite模式,可以将多个命令封装为一个“复合命令”MacroCommand。   

4.Command模式与C#中的Delegate有些类似。但两者定义行为接口的规范有所区别:Command以面向对象中的“接口-实现”来定义行为接口规范,更严格, 更符合抽象原则;Delegate以函数签名来定义行为接口规范,更灵活,但抽象能力比较弱。   

5.使用命令模式会导致某些系统有过多的具体命令类。某些系统可能需要几十个,几百个甚至几千个具体命令类,这会使命令模式在这样的系统里变得不实际。

适用性:    


    在下面的情况下应当考虑使用命令模式:   

1.使用命令模式作为"CallBack"在面向对象系统中的替代。"CallBack"讲的便是先将一个函数登记上,然后在以后调用此函数。   

2. 需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命期。换言之,原先的请求发出者可能已经不在了,而命令对象本身仍 然是活动的。这时命令的接收者可以是在本地,也可以在网络的另外一个地址。命令对象可以在串形化之后传送到另外一台机器上去。   

3.系统需要支持命令的撤消(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo()方法,把命令所产生的效果撤销掉。命令对象还可以提供redo()方法,以供客户端在需要时,再重新实施命令效果。   

4.如果一个系统要将系统中所有的数据更新到日志里,以便在系统崩溃时,可以根据日志里读回所有的数据更新命令,重新调用Execute()方法一条一条执行这些命令,从而恢复系统在崩溃前所做的数据更新。

参考:

1.《自己动手写Struts:构建基于MVC的Web开发框架》

2.《敏捷软件开发:原则、模式与实践》

3.《Head First设计模式》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  comman