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 查看评论
  • 相关阅读:
    POJ 3672 水题......
    POJ 3279 枚举?
    STL
    241. Different Ways to Add Parentheses
    282. Expression Add Operators
    169. Majority Element
    Weekly Contest 121
    927. Three Equal Parts
    910. Smallest Range II
    921. Minimum Add to Make Parentheses Valid
  • 原文地址:https://www.cnblogs.com/mxcy/p/3982110.html
Copyright © 2011-2022 走看看