假如公司计划开发一套MDM(移动设备管理)系统,即管理员可以在web页面上操控接入系统的手机(这里不区分Android和iOS),比如,针对某台手机,在web页面上点击“锁屏”按钮,该手机就锁屏了,点击“解锁”按钮,该手机就解锁了。
对于这套系统,核心部分无疑就是刚刚描述的一系列对手机下发命令。这套系统可以有许多不同的命令,锁屏和解锁只是其中的个例,那么有那么多的命令,实际应用中又设计到同时操控多台设备,在很短的时间内可能会产生许多命令,如果系统设计不当,很可能时效性就会收到影响,所以上面管理员类依赖于手机操控类,即命令(请求)的发送者与命令(请求)的执行者耦合这种简单的设计模式会有很大问题。
既然要管理许多命令,我们自然相当用list或者queue保存命令,基于某种策略执行命令,比如可以设置命令的优先级等等,这样,就自然需要一个负责命令调度的角色, 通过这个中间角色,来执行对手机的操控命令。通常这个中间角色叫做: invoker, 引入了invoker, 我们就需要重新设计一下这个系统,目标是解耦命令发送者和命令执行者,同时满足命令的可扩展性,保证整个系统的健壮性。命令模式,就是可以满足以上需求的设计模式。
1.命令模式
命令模式(Command);将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化,对请求排队或者对请求做日志记录,以及可以支持撤销的操作。 ----《大话设计模式》
我们使用命令模式来重新设计这套系统的核心部分:
命令模式把具体的命令(请求)封装为对象LockScreenCommand(锁屏命令)和UnlockScreenCommand(解锁命令), 锁屏和解锁一台手机,流程如下:
- Admin(管理员)创建锁屏命令LockScreenSommand对象和解锁屏命令对象UnlockScreenCommand;
- 管理员将这两个命令对象,给Invoker, Invoker将这两条命令放入列表中;
- 可以设置监听或者定时任务取激活Invoker取执行命令,但是我这里就简单在Admin中调用Invoker对象的inform()方法;
- Invoker的inform()方法中遍历命令,依次调用命令execute()方法执行;
- 命令的execute()调用MobileOperator(手机操控者)对象的action()方法,统一下发命令;
- 结束。
3.代码实现
定义抽象命令类, 也可以根据情况定义为interface。
/**
* 抽象命令类, 定义了执行命令的抽象方法。
* 关联于MobileOperator,进行命令的统一下发。
*/
abstract class Command {
protected MobileOperator operator;
public Command(MobileOperator operator){
this.operator = operator;
}
public abstract void execute();
}
定义命令下发类,该类被命令类所关联。
/**
* 进行命令统一下发的类
*/
class MobileOperator {
public void action(String commandName){
System.out.printf("下发【%s】命令 ",commandName);
}
}
然后定义两个具体的命令类,实现对手机的命令(请求)的封装。
/**
* 锁屏命令
*/
class LockScreenCommand extends Command {
public LockScreenCommand(MobileOperator operator) {
super(operator);
}
@Override
public void execute() {
operator.action("锁屏");
}
}
/**
* 解锁命令
*/
class UnlockScreenCommand extends Command {
public UnlockScreenCommand(MobileOperator operator) {
super(operator);
}
@Override
public void execute() {
operator.action("解锁");
}
}
定义Invoker, 这个类很关键,不仅解耦了请求者(Admin)和命令执行者(MobileOperator),还可以对命令的执行进行管理调度,可以扩展,现在只定义了增加命令方法。如果有需求,还可以撤销命令、调整命令顺序等等......
/**
* 命令调度
*/
class Invoker {
//这里姑且用ArrayList
List<Command> commandList = new ArrayList<>();
//添加命令
public void setCommand(Command command) {
commandList.add(command);
}
public void inform(){
for (Command command : commandList) {
command.execute();
}
}
}
定义命令(请求)的发送者---Admin, 作为入口执行命令
public class Admin { public static void main(String[] args) { // 手机操控对象 MobileOperator operator = new MobileOperator(); // 创建命令 Command lockCommand = new LockScreenCommand(operator); Command unLockCommand = new UnlockScreenCommand(operator); //添加命令 Invoker invoker = new Invoker(); invoker.setCommand(lockCommand); invoker.setCommand(unLockCommand); //执行命令 invoker.inform(); } }
输出结果:
下发【锁屏】命令
下发【解锁】命令
以上代码采用命令模式,实现了对手机下发命令,解耦了命令的发送者和执行者。
5.总结
通过一个场景:移动设备管理系统,应用命令模式对手机下发命令。通过上面的例子可以体会出命令模式,就是把命令(请求)封装成对象, 请求者任意生成若干个命令对象,把这些对象交给Invoker来管理,在实际项目中,通常请求者的工作到此就完成了,执行命令不需要请求者关系,通常由Invoker来进行调度,而本例为了简便,故在请求者Admin的main方法中调用了invoker.infom()。由于有Invoker这样一个“中间角色”的加入,系统的耦合度就降下来了,比如,我们可以扩展其它各种命令而不需要顾及到Admin,同时,还可以扩展Invoker的功能,实现命令按照自己需要的策略来执行,真实一石二鸟。
命令模式就是一把解决请求-执行这类模式的好工具,不过再好的工具也有其局限性,比如,常用的控制手机的命令也许只有十来种。倘若其它命令,有成百上千种,那么就要慎重使用命令模式了,光是实现这些命令就把人累死了,所以,当命令种类过多时,不适宜使用命令模式。