本文来自李明子csdn博客(http://blog.csdn.net/free1985),商业转载请联系博主获得授权,非商业转载请注明出处!
命令(Command)模式将请求的发送者与实际执行者解耦,使请求在指定的链路中传播,从而令多个对象均有机会处理同一请求。
命令模式有着广泛的应用,既可用于UI中多个事件绑定同一业务操作又可对命令进行重新排序、记录日志、维护请求序列以实现撤销操作等。
结合图2-1,下面介绍各类在命令设计模式中扮演的角色。
3.1 Command
Command是命令接口,声明了执行命令接口方法Execute。如果希望操作可以被撤销,则还需要声明反执行命令接口Unexcute。
3.2 ConcreteCommand
ConcreteCommand是具体命令,实现了命令接口Command。ConcreteCommand需要维护命令的接收者Receiver对象及执行命令时的必要参数。这些变量通常都是在构造方法中指定的。ConcreteCommand实现Execute接口方法时,调用Receiver的相应方法。如果需要实现Unexecute接口方法,则在实现Execute接口方法时还需记录执行操作前的上下文以便实现Unexecute接口方法时使用。
3.3 Invoker
Invoker是命令调用者,负责请求的发起。Invoker内部通常维护了Command类型的队列,以便在合适的时间执行命令。如果命令可以撤销,Invoker还应提供撤销方法以撤销最后一步操作。
3.4 Receiver
Receiver是命令接收者,是命令的实际执行者。
3.5 Client
Client是客户类,是命令模式的使用者。Client用适当的Receiver对象实例化Command 对象并将其委托给Invoker调度执行。
下面我们用一个业务场景实例来进一步讲解命令的使用。
4.1 场景介绍
某用户管理模块需要实现对用户的增、删、改、查操作。要求在这些操作记录日志并可以撤销。
以下各节将介绍该场景各类的具体实现及其在命令设计模式中所对应的参与者角色。
4.2 IUserOpCommand
IUserOpCommand是用户操作命令接口,声明了执行命令接口方法execute和反执行命令接口方法unexecute。对应于命令模式的参与者,IUserOpCommand是命令接口Command。下面的代码给出了IUserOpCommand的声明。
package demo.designpattern.command;
/**
* 用户操作命令接口
* Created by LiMingzi on 2017/7/29.
*/
public interface IUserOpCommand {
/**
* 执行操作
*/
void execute();
/**
* 撤销操作
*/
void unexecute();
/**
* 获取命令描述
* @return 命令描述
*/
String getCommandMsg();
}
上述代码中,22行,为了便于输出命令日志,声明了获取命令描述接口方法getCommandMsg。
4.3 InsUserCommand
InsUserCommand是插入用户命令,实现了IUserOpCommand接口。对应于命令模式的参与者,InsUserCommand是具体命令ConcreteCommand。下面的代码给出了InsUserCommand的声明。
package demo.designpattern.command;
/**
* 插入用户命令
* Created by LiMingzi on 2017/7/29.
*/
public class InsUserCommand implements IUserOpCommand {
/**
* 用户数据库访问对象
*/
private UserDao userDao;
/**
* 用户id
*/
private String userId=null;
/**
* 用户名
*/
private String userName=null;
/**
* 是否已执行
*/
boolean hasExecuted=false;
/**
* 构造方法
* @param userDao 用户数据库访问对象
* @param id 用户id
* @param name 用户名
*/
public InsUserCommand(UserDao userDao,String id, String name) {
this.userDao = userDao;
this.userId=id;
this.userName=name;
}
/**
* 执行操作
*/
@Override
public void execute() {
if(!hasExecuted){
userDao.insertUserInfo(userId,userName);
hasExecuted = true;
}
}
/**
* 撤销操作
*/
@Override
public void unexecute() {
if(hasExecuted) {
userDao.delUser(userId);
hasExecuted = false;
}
}
/**
* 获取命令描述
*
* @return 命令描述
*/
@Override
public String getCommandMsg() {
return "命令:插入id为'"+userId+"'的用户";
}
}
上述代码中,32行,构造方法有3个参数,分别是命令接收对象userDao及执行命令需要的用户id和用户名。另外,为了避免操作重复执行,24行声明了表示命令是否已被执行的成员变量hasExecuted。
4.4 DelUserCommand
DelUserCommand是删除用户命令,实现了IUserOpCommand接口。对应于命令模式的参与者,DelUserCommand是具体命令ConcreteCommand。下面的代码给出了DelUserCommand的声明。
package demo.designpattern.command;
/**
* 删除用户命令
* Created by LiMingzi on 2017/7/29.
*/
public class DelUserCommand implements IUserOpCommand {
/**
* 用户数据库访问对象
*/
private UserDao userDao;
/**
* 用户id
*/
private String userId=null;
/**
* 用户名
*/
private String userName=null;
/**
* 是否已执行
*/
boolean hasExecuted=false;
/**
* 构造方法
* @param userDao 用户数据库访问对象
* @param id 用户id
*/
public DelUserCommand(UserDao userDao,String id) {
this.userDao = userDao;
this.userId=id;
}
/**
* 执行操作
*/
@Override
public void execute() {
if(!hasExecuted){
this.userName=userDao.getUserName(userId);
userDao.delUser(userId);
hasExecuted=true;
}
}
/**
* 撤销操作
*/
@Override
public void unexecute() {
if(hasExecuted){
userDao.insertUserInfo(userId,userName);
userName = null;
hasExecuted = false;
}
}
/**
* 获取命令描述
*
* @return 命令描述
*/
@Override
public String getCommandMsg() {
return "命令:删除id为'"+userId+"'的用户";
}
}
上述代码中,为了实现unexecute接口方法,20行,声明了表示用户名的成员变量userName。该变量在execute方法中(42)行赋值,并在unexecute方法中(54行)被使用。
4.5 UdUserCommand
UdUserCommand是更新用户命令,实现了IUserOpCommand接口。对应于命令模式的参与者,UdUserCommand是具体命令ConcreteCommand。下面的代码给出了UdUserCommand的声明。
package demo.designpattern.command;
/**
* 更新用户命令
* Created by LiMingzi on 2017/7/29.
*/
public class UdUserCommand implements IUserOpCommand {
/**
* 用户数据库访问对象
*/
private UserDao userDao;
/**
* 用户id
*/
private String userId=null;
/**
* 新用户名
*/
private String newUserName=null;
/**
* 旧用户名
*/
private String oldUserName=null;
/**
* 是否已执行
*/
boolean hasExecuted=false;
/**
* 构造方法
* @param userDao 用户数据库访问对象
* @param id 用户id
* @param name 用户名
*/
public UdUserCommand(UserDao userDao,String id, String name) {
this.userDao = userDao;
this.userId=id;
this.newUserName=name;
}
/**
* 执行操作
*/
@Override
public void execute() {
if(!hasExecuted){
oldUserName = userDao.getUserName(userId);
userDao.updateUserInfo(userId,newUserName);
hasExecuted = true;
}
}
/**
* 撤销操作
*/
@Override
public void unexecute() {
if(hasExecuted) {
userDao.updateUserInfo(userId,oldUserName);
oldUserName=null;
hasExecuted = false;
}
}
/**
* 获取命令描述
*
* @return 命令描述
*/
@Override
public String getCommandMsg() {
return "命令:将id为'"+userId+"'的用户名更新为'"+newUserName+"'";
}
}
4.6 UserOpInvoker
UserOpInvoker是用户操作请求类。对应于命令模式的参与者,UserOpInvoker是命令调用者Invoker。下面的代码给出了UserOpInvoker的声明。
package demo.designpattern.command;
import java.util.ArrayList;
import java.util.List;
/**
* 用户操作调用者
* Created by LiMingzi on 2017/7/29.
*/
public class UserOpInvoker{
/**
* 已执行的用户操作命令集合
*/
private List doneCommands=new ArrayList();
/**
* 添加用户操作命令
* @param userOpCommand
*/
public void addCommand(IUserOpCommand userOpCommand){
System.out.println(new java.util.Date().toString()+" 执行"+userOpCommand.getCommandMsg());
userOpCommand.execute();
doneCommands.add(userOpCommand);
}
/**
* 撤销上一步操作
*/
public void undo(){
if (doneCommands.size() > 0) {
// 要撤销的命令
IUserOpCommand undoCommand = doneCommands.get(doneCommands.size() - 1);
System.out.println(new java.util.Date().toString()+" 撤销"+undoCommand.getCommandMsg());
undoCommand.unexecute();
doneCommands.remove(undoCommand);
}
}
}
上述代码中,14行,成员变量 doneCommands维护了已执行的用户操作命令集合,以便32行撤销上一步操作undo方法获取最后一步操作。20行,添加用户操作命令方法addCommand在输出日志后立即执行了命令。在实际场景中,该命令可能不会被立即执行,根据需求可能会追加到待执行命令队列,并在单独的线程中从命令队列摘取任务执行。
4.7 UserDao
UserDao是用户数据访问对象类,提供对数据库的增、删、改、查操作。对应于命令模式的参与者,UserDao是命令接收者Receiver。下面的代码给出了UserDao的声明。
package demo.designpattern.command;
import java.util.HashMap;
import java.util.Map;
/**
* 用户数据访问对象
* Created by LiMingzi on 2017/7/29
*/
public class UserDao {
/**
* 用户字典,demo,代替数据库
*/
private static Map userMap = new HashMap();
/**
* 获取用户名
*
* @param id 用户id
* @return 用户名
*/
public String getUserName(String id) {
// 访问数据库
System.out.println("访问数据库:select user_id,user_name from t_user where user_id = '"+id+"'");
// 用户名
String name =userMap.get(id);
return name;
}
/**
* 更新用户信息
*
* @param id 用户id
* @param name 用户名
*/
public void updateUserInfo(String id, String name) {
// 访问数据库
System.out.println("访问数据库:update t_user set user_name = '"+name+"' where user_id = '"+id+"'");
userMap.put(id,name);
}
/**
* 删除用户
*
* @param id 用户id
*/
public void delUser(String id) {
// 访问数据库
System.out.println("访问数据库:delete from t_user where user_id = '"+id+"'");
userMap.remove(id);
}
/**
* 插入用户信息
*
* @param id 用户id
* @param name 用户名
*/
public void insertUserInfo(String id, String name) {
// 访问数据库
System.out.println("访问数据库:insert into t_user(user_id,user_name) values('"+id+"','"+name+"')");
userMap.put(id,name);
}
}
上述代码中的各数据库操作均为演示方法,输出虚拟的sql语句,实际操作为对map的处理。
4.8 UserMgmt
UserMgmt是用户管理类,处理用户的增、删、改操作及撤销操作。对应于命令模式的参与者,UserMgmt是客户Client。下面的代码给出了UserMgmt的声明。
package demo.designpattern.command;
/**
* 用户管理类
* Created by LiMingzi on 2017/7/29.
*/
public class UserMgmt {
/**
* 用户操作调用者
*/
private UserOpInvoker userOpInvoker = new UserOpInvoker();
/**
* 用户数据库操作对象
*/
private UserDao userDao = new UserDao();
/**
* 添加用户
* @param id 用户id
* @param name 用户名
*/
public void insertUser(String id,String name){
// 用户操作命令
IUserOpCommand userOpCommand = new InsUserCommand(userDao,id,name);
userOpInvoker.addCommand(userOpCommand);
}
/**
* 删除用户
* @param id 用户id
*/
public void deleteUser(String id){
// 用户操作命令
IUserOpCommand userOpCommand = new DelUserCommand(userDao,id);
userOpInvoker.addCommand(userOpCommand);
}
/**
* 更新用户
* @param id 用户id
* @param name 用户名
*/
public void updateUser(String id,String name){
// 用户操作命令
IUserOpCommand userOpCommand = new UdUserCommand(userDao,id,name);
userOpInvoker.addCommand(userOpCommand);
}
/**
* 撤销上一步操作
*/
public void undo(){
userOpInvoker.undo();
}
}
上述代码中,11行声明了用户操作调用者对象userOpInvoker。15行维护了作为命令接收者的成员变量——用户数据库操作对象userDao。在本例中,各命令使用相同的接收者,在实际的应用场景中并不都是这样的。22行,添加用户方法insertUser,使用InsUserCommand类对象实例化IUserOpCommand接口,传入接收者userDao。再将命令通过addCommand方法传递给命令调用者userOpInvoker。
4.9 测试代码
为了测试本文中的代码,我们可以编写如下测试代码。测试代码中分别创建和执行了新增用户、修改用户、删除用户三个命令,又逐一撤销了这些命令。
/**
* 命令测试
*/
public static void commandTest(){
// 用户管理对象
demo.designpattern.command.UserMgmt userMgmt = new demo.designpattern.command.UserMgmt();
userMgmt.insertUser("001","张三");
userMgmt.updateUser("001","张三(小)");
userMgmt.deleteUser("001");
userMgmt.undo();
userMgmt.undo();
userMgmt.undo();
}
编译运行后,得到如下测试结果:
Mon Jul 31 17:31:43 CST 2017 执行命令:插入id为’001’的用户
访问数据库:insert into t_user(user_id,user_name) values(‘001’,’张三’)
Mon Jul 31 17:31:43 CST 2017 执行命令:将id为’001’的用户名更新为’张三(小)’
访问数据库:select user_id,user_name from t_user where user_id = ‘001’
访问数据库:update t_user set user_name = ‘张三(小)’ where user_id = ‘001’
Mon Jul 31 17:31:43 CST 2017 执行命令:删除id为’001’的用户
访问数据库:select user_id,user_name from t_user where user_id = ‘001’
访问数据库:delete from t_user where user_id = ‘001’
Mon Jul 31 17:31:43 CST 2017 撤销命令:删除id为’001’的用户
访问数据库:insert into t_user(user_id,user_name) values(‘001’,’张三(小)’)
Mon Jul 31 17:31:43 CST 2017 撤销命令:将id为’001’的用户名更新为’张三(小)’
访问数据库:update t_user set user_name = ‘张三’ where user_id = ‘001’
Mon Jul 31 17:31:43 CST 2017 撤销命令:插入id为’001’的用户
访问数据库:delete from t_user where user_id = ‘001’