介绍
行为型设计模式之一。这个模式没有规矩,比较灵活。将一些列的方法调用封装,用户只需要调用一个方法执行,那么所有这些被封装的方法就会被挨个执行调用。如名字一样,命令,比如开机命令,只需要一条命令,内部操作都封装好了自己执行。很多时候封装API其实也可以看作是一个简单的命令
Android中使用多,但不是很典型,比较典型的是Android的事件机制中底层逻辑对事件的转发处理,Android的每一种事件在屏幕上产生后都会经由底层逻辑将其转化为一个NotifyAgs对象,NotifyAgs相当于一个命令者抽象。InputDispatcher则为请求者,接受者角色由硬件驱动承担
UML
- Receiver:接受者角色,负责具体实施或执行一个请求,执行具体逻辑的角色
- Command:命令角色,定义所有具体命令类的抽象接口
- ConcreteCommand: 具体命令角色,相当于一个中间角色,调用接受者的相关方法,在接受者和命令执行的具体行为之间加以弱耦合
- Invoker: 请求者角色,该类职责就是调用命令对象执行具体的请求,相关方法称为行动方法
- Client: 客户端角色
使用场景
- 需要抽象待执行操作,以参数形式提供出来——类似于过程设计中的回调机制,而命令模式正是回调机制的一个面向对象的替代品
- 在不同的时刻指定、排列和执行请求。一个命令对象可以有与初始化请求无关的生存期
- 需要支持取消操作
- 支持修改日志功能,这样当系统崩溃时,这些修改可以被重做一遍
- 需要支持事务操作
事例
以小明吃饭为例子,采用命令模式的结构来进行操作。
- 命令抽象:提供吃和取消吃方法
/**
* 命令模式命令
*/
public interface ICommand {
/**
* 吃
*/
void eat();
/**
* 取消吃
*/
void cancelEat();
}
- 创建处理者:可实现接口,也可不实现,目的还是提供给命令调用
/**
* 小明命令处理
*/
public class XiaoMingResolver implements ICommand {
EatThread eatThread;
public XiaoMingResolver() {
}
/**
* 吃饭中标志
*/
private boolean eating;
/**
* 吃饭操作
*/
@Override
public void eat() {
eating = true;
//开启线程输出吃饭
eatThread = new EatThread(this);
eatThread.start();
}
/**
* 取消吃饭
*/
@Override
public void cancelEat() {
try {
//终止吃饭
eating = false;
eatThread.join();
eatThread = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static class EatThread extends Thread {
private WeakReference<XiaoMingResolver> xiaoMingResolverWeakReference;
public EatThread(XiaoMingResolver xiaoMingResolver) {
xiaoMingResolverWeakReference = new WeakReference<XiaoMingResolver>(xiaoMingResolver);
}
@Override
public void run() {
super.run();
/*每隔一秒输出一次吃饭中*/
XiaoMingResolver xiaoMingResolver = xiaoMingResolverWeakReference.get();
while (xiaoMingResolver != null && xiaoMingResolver.eating) {
try {
Thread.sleep(1000);
System.out.println("小明吃饭中");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
- 小明实际命令:实现接口
/**
* 小明命令实现
*/
public class XiaoMingEatCommand implements ICommand {
private XiaoMingResolver xiaoMingResolver;
public XiaoMingEatCommand() {
xiaoMingResolver = new XiaoMingResolver();
}
@Override
public void eat() {
//调用处理者的吃
xiaoMingResolver.eat();
}
@Override
public void cancelEat() {
//调用处理者的取消吃
xiaoMingResolver.cancelEat();
}
}
- 命令请求者:持有命令,在调用命令方法
/**
* 命令请求者
*/
public class ComInvoker {
private ICommand iCommand;
public ComInvoker(ICommand iCommand) {
this.iCommand = iCommand;
}
/**
* 执行吃操作
*/
public void exeEat() {
iCommand.eat();
}
/**
* 执行取消吃操作
*/
public void exeCancelEat() {
iCommand.cancelEat();
}
}
- 测试:
ICommand xiaoMingEatCommand = new XiaoMingEatCommand();
ComInvoker comInvoker = new ComInvoker(xiaoMingEatCommand);
comInvoker.exeEat();
try {
Thread.sleep(3000);
comInvoker.exeCancelEat();
//再开始吃
comInvoker.exeEat();
Thread.sleep(3000);
comInvoker.exeCancelEat();
} catch (InterruptedException e) {
e.printStackTrace();
}
- 输出
小明吃饭中
小明吃饭中
小明吃饭中
再开始吃
小明吃饭中
小明吃饭中
小明吃饭中
这里的操作很简单,看似简单的操作,硬生生分了成了几部分:
- 请求者持有命令
- 由具体的命令者持有最终实现者
- 最终实现者实现代码
总结:命令模式的思想就是,调用方一条命令下去,剩下的内部去执行就好,而且内部还做到了低耦合和可替换。它的缺点很明显,类的膨胀,产生类很多衍生类。优点也非常明显,更弱的耦合、更灵活的控制性和扩展性,比如最终实现者或者是具体命令者都是随时可替换的,实际过程要不要用它还是可以多考虑考虑。