事实上在项目开发中,我们使用了大量的设计模式,不过这些设计模式都封装在框架中了,假设你想要不只局限于简单的使用,就应该深入了解框架的设计思路。
在MVC框架中,模式之中的一个就是命令模式,先来看看模式是怎样定义的。
命令模式:
定义:把一个请求或者操作封装在命令对象中。命令模式同意系统使用不同的请求把client參数化,对请求排队或者记录请求日志,能够提供命令的撤销和恢复功能。
Invoker类被client调用,能够接受命令请求,设计命令队列,决定是否对应该请求,记录或撤销或重做命令请求,记录日志等等.
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框架大致的流程是:首先client发送请求,提交JSP页面给中转器(Servlet);中转器依据客户的请求,选择对应的模型层,即Logic,Logic进行对应的逻辑处理;假设须要使用数据库,则通过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.使用命令模式会导致某些系统有过多的详细命令类。某些系统可能须要几十个,几百个甚至几千个详细命令类,这会使命令模式在这种系统里变得不实际。