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

    命令模式:

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

    7d8e0742-c5d2-30aa-9e98-2922efcbc1fb

    Invoker类 被client调用,能够接受命令请求。设计命令队列。决定是否对应该请求,记录或撤销或重做命令请求。记录日志等等.

      1. public class Invoker {   
      2. private Command command;   
      3. public void setOrder(Command command) {   
      4. this.command = command;   
      5.     }   
      6. public void ExecuteCommand() {   
      7.         command.ExecuteCommand();   
      8.     }   
      9. }   
       

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

      1. public abstract class Command {   
      2. protected Receiver receiver;   
      3. public Command(Receiver receiver){   
      4. this.receiver = receiver;   
      5.     }   
      6. public abstract void ExecuteCommand();   
      7. }   
       

    ConcreteCommand类,能够将Receiver对象放到这个类里面。这个类详细实现了要怎么处理这个用户的请求。

      1. public class ConcreteCommand extends Command {   
      2. public ConcreteCommand(Receiver receiver){   
      3. super(receiver);   
      4.     }   
      5. @Override  
      6. public void ExecuteCommand() {   
      7.         receiver.Execute();   
      8.     }   
      9. }   
       

    Receiver类,事实上这个类能够没有,只是为了让设计看起来更整洁清楚。

      1. public class Receiver {   
      2. public void Execute(){   
      3.         System.out.println("Receiver excute!");   
      4.     }   
      5. }   
       

    最后一个Client类。

      1. public class Client {   
      2. public static void main(String[] args) {   
      3.         Receiver r = new Receiver();   
      4.         Command c = new ConcreteCommand(r);    
      5.         Invoker i = new Invoker();   
      6.         i.setOrder(c);   
      7.         i.ExecuteCommand();   
      8.     }   
      9. }   
       

    命令模式在MVC中的应用:

          Struts中,在模型层都要继承一个Action接口,并实现execute方法,事实上这个Action就是命令类。为什么Struts会应用命令模式,是由于Struts的核心控制器ActionServlet仅仅有一个,相当于Invoker,而模型层的类会随着不同的应用有不同的模型类,相当于详细的Command。

    这样,就须要在ActionServlet和模型层之间解耦,而命令模式正好解决问题。

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

    image002

    说明:

    • 视图层採用JSP实现
    • 控制器採用Servlet实现,整个框架採用同一个Servlet,以实现请求的中转
    • 模型层採用Java实现,主要决定用来做什么
    • 在模型层后加入了一个DAO。目的是将决定做什么和详细怎么做分开

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

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

    1.控制层设计

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

          在Servlet中,将使用doPost()来处理对应的中转请求,假设开发者使用get提交方式,则使用例如以下方式进行处理。演示样例代码例如以下:

    1. public void doGet(HttpServletRequest req, HttpServletResponse res)  
    2. throws ServletException, IOException {  
    3.     doPost(req, res);  
    4. }  
    5. //使用post提交方式  
    6. public void doPost(HttpServletRequest req, HttpServletResponse res)  
    7. throws ServletException, IOException {  
    8.     do_Dispatcher (req, res);  
    9. }  
     

    代码说明:

    • 不论採用get还是post提交方式,都将运行do_Dispatcher(req, res)方法。

    • do_Dispatcher(req, res)是用来处理视图层发送来的请求的方法。

          假设直接使用request方式来获取从页面提交的数据,在要获取的数据比較多的情况下。会比較烦琐。并且直接将request传递给模型层不符合Model 2规范。所以,这里将对从页面传来的值进行封装。将其放在一个Map中。然后再传递给模型层。这样在模型层就能够直接使用Map中的值。演示样例代码例如以下:

    1. private HashMap getRequestToMap(HttpServletRequest req) throws Exception {  
    2.     req.setCharacterEncoding("GBK");  
    3.         HashMap infoIn = new HashMap();  
    4.         for (Enumeration e = req.getParameterNames(); e.hasMoreElements ();)  
    5.                 {//获取页面中全部元素名  
    6.             String strName = (String)e.nextElement();  
    7.             String[] values = (String[]) req.getParameterValues (strName);  
    8.                 //依据名称获取相应的值  
    9.             if (values == null) {//假如没有值  
    10.                 infoIn.put(strName, "");  
    11.             } else if (values.length == 1) {//假如仅仅有一个值  
    12.                 infoIn.put(strName, values[0]);  
    13.             } else {//假如有多个值  
    14.                 infoIn.put(strName, values);  
    15.             }  
    16.         }  
    17.     return infoIn;  
    18. }  
     

    代码说明:

    • req.setCharacterEncoding("GBK"),这里首先将从视图层传来的数据设定编码为GBK。
    • HashMap infoIn = new HashMap(),定义一个HashMap,用来存放从request中获取的数据。
    • req.getParameterNames()。用来获取从页面中传来的全部元素。
    • req.getParameterValues(),用来依据元素名称来获取元素相应的值,并将元素名称和值的相应关系存入HashMap中。假设元素的值为空,则在HashMap中将元素名称相应的值置为空。假设仅仅有一个值。则将该值存入;假设有多个值,则存入数组。

    命令模式使用:

    一个视图相应一个模型,也可能一个视图相应多个模型。但唯独一个控制器。所以,为了实现一个控制器能够转发到多个模型中去,就须要使用接口,让全部模型都实现这个接口。然后在控制器里。不过面对接口编程就可以。

    这里定义一个接口Action.java。Action.java的演示样例代码例如以下:

    1. //******* Action.java**************  
    2. import java.util.*;  
    3. public interface Action{  
    4.     public HashMap doAction(HashMap infoIn);  
    5. }  
     

    在控制器中仅仅针对这个接口处理就可以。演示样例代码例如以下:

    1. Actionaction = (Action) Class.forName(getActionName(systemName,  
    2. logicName)).newInstance();  
    3. HashMap infoOut = action.doAction(infoIn);  
     

    代码说明:

    • getActionName()方法是获取实现接口Action的类的名称和所在的包。演示样例代码例如以下:
    1. private String getActionName(String systemName ,String actionName)  
    2. throws IOException, Exception {  
    3. return "com. " + systemName + ".action." + actionName;  
    4. }  
     

    使用RequestDispatcher返回视图层。

    演示样例代码例如以下:

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

    代码说明:

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

    2.模型层设计

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

    演示样例代码例如以下:

    1. //******* WebExamAction.java**************  
    2. import java.util.HashMap;  
    3. public class WebExamAction implements Action{  
    4.     //依据页面的请求,进行动作的转换  
    5.     public HashMap doAction(HashMap infoIn) {  
    6.         String action = (infoIn.get("action") == null) ? "" :  
    7. (String)infoIn.get("action");  
    8.         HashMap infoOut = new HashMap();  
    9.         if (action.equals(""))           infoOut = this.doInit (infoIn);  
    10.         else if (action.equals("insert")) infoOut = this.doInsert (infoIn);  
    11.         return infoOut;  
    12.     }  
    13.     /**该方法设置用户登录时页面的初始信息  
    14.     * @param infoIn  
    15.     * @return HashMap  
    16.     */  
    17.     private HashMap doInit(HashMap infoIn) {  
    18.         HashMap infoOut = infoIn;  
    19.         int clerkId = (infoIn.get("clerkId") == null || "".equals  
    20. (infoIn.get("clerkId"))) ?

       -1 : Integer.parseInt((String)  

    21. infoIn.get ("clerkId"));  
    22.         try {  
    23.             //取得考生姓名  
    24.             Users user = new Users();  
    25.             String clerkName = user.getName(clerkId);   
    26.             infoOut.put("clerkName", clerkName);  
    27.         } catch(Exception e) {  
    28.             e.printStackTrace();  
    29.         } finally {  
    30.             return infoOut;  
    31.         }  
    32.     }  
    33.     /**该方法用来进行新增 
    34.     * @param infoIn 
    35.     * @return HashMap 
    36.     */  
    37.     private HashMap doInsert(HashMap infoIn) {  
    38.         HashMap infoOut = infoIn;  
    39.         int clerkId = (infoIn.get("clerkId") == null || "".equals  
    40. (infoIn.get("clerkId"))) ?

       -1 : Integer.parseInt((String)  

    41. infoIn.get ("clerkId"));  
    42.         try {  
    43.             //取得考生姓名  
    44.             Users user = new Users();  
    45.             String clerkName = user.getName(clerkId);   
    46.             infoOut.put("clerkName", clerkName);  
    47.         } catch(Exception e) {  
    48.             e.printStackTrace();  
    49.         } finally {  
    50.             return infoOut;  
    51.         }  
    52.     }  
    53. }  
     

    代码说明:

    • 这里,在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)。命令对象能够把状态存储起来,等到client须要撤销命令所产生的效果时。能够调用undo()方法。把命令所产生的效果撤销掉。

    命令对象还能够提供redo()方法,以供client在须要时,再又一次实施命令效果。 
    4.假设一个系统要将系统中全部的数据更新到日志里。以便在系统崩溃时。能够依据日志里读回全部的数据更新命令。又一次调用Execute()方法一条一条运行这些命令,从而恢复系统在崩溃前所做的数据更新。

    參考:

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

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

    3.《Head First设计模式》

  • 相关阅读:
    javaweb:Filter过滤器
    javaScript:高级
    javascript:基础
    Boostrao:轮播图
    Bootstrap案列:首页界面
    Bootstrap学习笔记
    javaweb:respone
    javaweb:jsp
    五、结构型模式--->07.享元模式
    五、结构型模式--->06.组合模式
  • 原文地址:https://www.cnblogs.com/jhcelue/p/6785191.html
Copyright © 2011-2022 走看看