zoukankan      html  css  js  c++  java
  • [原]命令模式在MVC框架中的应用

    其实在项目开发中,我们使用了大量的设计模式,只是这些设计模式都封装在框架中了,如果你想要不仅仅局限于简单的使用,就应该深入了解框架的设计思路。

    在MVC框架中,模式之一就是命令模式,先来看看模式是如何定义的。


    命令模式:

     

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

     


    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框架示意图:


    说明:

    控制器采用Servlet实现,整个框架采用同一个Servlet,以实现请求的中转

    Controller Servlet包括两部分:处理程序和命令


     

          整个Web框架大致的流程是:首先客户端发送请求,提交JSP页面给中转器(Servlet);中转器根据客户的请求,选择相应的模型层,即LogicLogic进行相应的逻辑处理;如果需要使用数据库,则通过DAO进行相应的数据库操作。

     

     

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

    控制层设计

          控制层主要用来转发从视图层传来的数据和请求到相对应的模型层,因此,实现它最好的方式莫过于使用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)是用来处理视图层发送来的请求的方法。

         这样的做体现了Front Controller模式,如果直接使用request方式来获取从页面提交的数据,在要获取的数据比较多的情况下,会比较烦琐,而且直接将request传递给模型层不符合Model2规范。所以,这里将对从页面传来的值进行封装,将其放在一个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的类的名称和所在的包。

     

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

    req.setAttribute("infoOut", infoOut);  
    RequestDispatcher rd = req.getRequestDispatcher("/"+ systemName +  
    "/jsp/" + forwardJsp+ ".jsp");  
    rd.forward(req, res);  

                      代码说明:

    这里表示JSP文件放在项目中系统名下的jsp文件夹下。

     

    模型层设计

    假定有一个模型层类为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.使用命令模式会导致某些系统有过多的具体命令类。某些系统可能需要几十个,几百个甚至几千个具体命令类,这会使命令模式在这样的系统里变得不实际。



    作者:tcl_6666 发表于2014-7-30 16:58:47 原文链接
    阅读:281 评论:5 查看评论
  • 相关阅读:
    Silverlight 4.0添加鼠标右键菜单和Silverlight全屏模式的进入退出
    获取天气服务
    Silverlight 数据绑定 (1):怎样实现数据绑定 &&Silverlight 数据绑定 (2):Source to Target
    调用根据IP查看城市WebService
    Silverlight中的Binding使用(一、二、三)
    使用Prism实现的WPF MVVM点餐Demo
    [Silverlight入门系列]实现局部元素全屏(Element部分全屏)
    silverlight 上下标
    动画教程(动态注册/静态注册)
    Silverlight数据验证
  • 原文地址:https://www.cnblogs.com/mxcy/p/3982110.html
Copyright © 2011-2022 走看看