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

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

命令模式:

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

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

  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. }
  8.  
  9. ConcreteCommand类,可以将Receiver对象放到这个类里面,这个类具体实现了要怎么处理这个用户的请求。
  10. public class ConcreteCommand extends Command {
  11. public ConcreteCommand(Receiver receiver){
  12. super(receiver);
  13. }
  14. @Override
  15. public void ExecuteCommand() {
  16. receiver.Execute();
  17. }
  18. }

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

说明:

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

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

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

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

控制层设计

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

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

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

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

代码说明:

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

模型层设计

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

作者:tcl_6666 发表于2014-7-30 16:58:47 原文链接
阅读:281 评论:5 查看评论

[原]命令模式在MVC框架中的应用的更多相关文章

  1. 命令模式在MVC框架中的应用

    事实上在项目开发中,我们使用了大量的设计模式,不过这些设计模式都封装在框架中了,假设你想要不只局限于简单的使用,就应该深入了解框架的设计思路. 在MVC框架中,模式之中的一个就是命令模式,先来看看模式 ...

  2. MVC框架中的值提供机制(二)

    在MVC框架中存在一些默认的值提供程序模板,这些值提供程序都是通过工厂模式类创建;在MVC框架中存在需要已Factory结尾的工厂类,在值提供程序中也存在ValueProviderFactories工 ...

  3. 设计模式(十五)——命令模式(Spring框架的JdbcTemplate源码分析)

    1 智能生活项目需求 看一个具体的需求 1) 我们买了一套智能家电,有照明灯.风扇.冰箱.洗衣机,我们只要在手机上安装 app 就可以控制对这些家电工作. 2) 这些智能家电来自不同的厂家,我们不想针 ...

  4. 找到MVC框架中前端URL与后端同步的解决方案

    基本思路: 先用URL标签生成完整的URL字符,前端动态参数的部分以适配符先填充,最后动态参数利用正则匹配进行替换. 这种方式,可以在各种MVC框架中适用,妙. 不废话,上码. var url = & ...

  5. asp.net MVC 框架中控制器里使用Newtonsoft.Json对前端传过来的字符串进行解析

    下面我用一个实例来和大家分享一下我的经验,asp.net MVC 框架中控制器里使用Newtonsoft.Json对前端传过来的字符串进行解析. using Newtonsoft.Json; usin ...

  6. 在ASP.NET MVC 框架中调用 html文件及解析get请求中的参数值

    在ASP.NET MVC 框架中调用 html文件: public ActionResult Index() { using (StreamReader sr = new StreamReader(P ...

  7. MVC框架中的值提供机制(三)

    在MVC框架中NameValueCollectionValueProvider采用一个NameValueCollection作为数据源,DictionnaryValueProvider的数据源类型自然 ...

  8. MVC框架中的值提供机制(一)

    在MVC框架中action方法中的Model数据的绑定的来源有很多个,可能是http请求中的get参数或是post提交的表单数据,会是json字符串或是路径中的相关数据;MVC框架中针对这些不同的数据 ...

  9. MVC 框架中的缓存

    在程序中加入缓存的目的很多是为了提高程序的性能,提高数据的查找效率,在MVC框架中也引入了非常多的缓存,比如Controller的匹配查找,Controller,ControllerDescripto ...

随机推荐

  1. Java Notes 00 - Singleton Pattern(单例总结)

    转:http://hukai.me/java-notes-singleton-pattern/ 这里不赘述单例模式的概念了,直接演示几种不同的实现方式. 0)Eager initialization ...

  2. js cookies存取删操作实例

    //写cookies函数 function SetCookie(name,value)//两个参数,一个是cookie的名子,一个是值 { var Days = 30; //此 cookie 将被保存 ...

  3. 网络解析 get 和post

    //get同步 - (IBAction)getT:(id)sender { //准备一个Url NSURL *url=[NSURL URLWithString:BASE_URL]; //创建一个请求对 ...

  4. MYSQL 二进制还原

    解决方法: mysqlbinlog bin_log_file_path_and_name | mysql -uroot -p 如: mysqlbinlog E:\DB\mysql_log\mysql_ ...

  5. C#dll版本号默认生成规则

    原文:C#dll版本号默认生成规则 1.版本号自动生成方法 只需把 AssemblyInfo.cs文件中的[assembly: AssemblyVersion("1.0.0.0") ...

  6. chapter 10 统计检验

    1.permutation test 用途:用于检验两组数据是否出生于同一分布 思路:如果产生于同一分布,两组数据混合,重新排列后,计算的基于两组数据的函数值(均值,中位数,方差等,下面程序中使用f指 ...

  7. 实现多文件上传在iOS开发中

    该功能实现了实现多文件上传在iOS开发中,喜欢的朋友可以研究一下吧. NSURL* url = [NSURL URLWithString:@"xxx"]; ASIFormDataR ...

  8. cocos2d-x for wp8 设置横竖屏

    在主project文件(xxx.cpp , xxx为你的项目名)中, 函数名为void xxx::SetWindow(CoreWindow^ window) 相关代码片例如以下: <pre na ...

  9. Spring IOC的描述和Spring的注解(转)

    Spring常用的注解 本文系转载:转载网址: http://www.cnblogs.com/xdp-gacl/p/3495887.html http://ljhzzyx.blog.163.com/b ...

  10. Linux下终端利器tmux(转)

    “君子生非异也,善假于物也” .–语出<荀子·劝学> 如果记得没错的话,<荀子·劝学>我们这一代高中的时候应该都读过这篇文章.原意大概是君子的资质与一般人没有什么区别,君子之所 ...