模式的定义
命令模式是一个高内聚的模式,定义如下:
Encapsulate a request as an object,thereby letting you parameterize clients with different requests,queue or log requests,and support undoable operations
将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录日志,可以提供命令的撤销和恢复功能。
模式的使用场景
只要是你认为是命令的地方,就可以采用命令模式
UML类图
角色介绍
- Receiver接收者角色
命令接收者模式,命令传递到这里执行对应的操作。
- Command命令角色
需要执行的命令都在这里声明 - Invoker调用者角色
接收到命令,并执行命令,也就是命令的发动者和调用者
命令模式封装性比较好,比较简单,项目中使用比较频繁。它把命令请求方(Invoker)和执行方(Receiver)分开,扩展性好。
模式的通用源码
通用Receiver类:
public abstract class Receiver {
//抽象接收者,定义每个接收者都必须完成的逻辑
public abstract void doSomething();
}
具体Receiver类:
public class ConcreteReceiver1 extends Receiver{
@Override
public void doSomething() {
// TODO Auto-generated method stub
System.out.println("ConcreteReceiver1---doSomething");
}
}
public class ConcreteReceiver2 extends Receiver{
@Override
public void doSomething() {
// TODO Auto-generated method stub
System.out.println("ConcreteReceiver2---doSomething");
}
}
抽象Command类:
public abstract class Command {
//每个命令类都必须有一个执行命令的方法
public abstract void execute();
}
具体Command类:
public class ConcreteCommand1 extends Command {
//对哪个receiver类进行命令处理
private Receiver receiver;
public ConcreteCommand1(Receiver receiver) {
super();
this.receiver = receiver;
}
//必须实现一个命令
@Override
public void execute() {
// TODO Auto-generated method stub
System.out.println("ConcreteCommand1---execute");
receiver.doSomething();
}
}
public class ConcreteCommand2 extends Command {
//对哪个receiver类进行命令处理
private Receiver receiver;
public ConcreteCommand2(Receiver receiver) {
super();
this.receiver = receiver;
}
//必须实现一个命令
@Override
public void execute() {
// TODO Auto-generated method stub
System.out.println("ConcreteCommand2---execute");
receiver.doSomething();
}
}
调用者Invoker类:
public class Invoker {
private Command command;
//接收命令
public void setCommand(Command command){
this.command = command;
}
//执行命令
public void action(){
this.command.execute();
}
}
场景Client类:
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
Receiver receiver1 = new ConcreteReceiver1();
Command command1 = new ConcreteCommand1(receiver1);
Invoker invoker = new Invoker();
invoker.setCommand(command1);
invoker.action();
Receiver receiver2 = new ConcreteReceiver2();
Command command2 = new ConcreteCommand2(receiver2);
invoker.setCommand(command2);
invoker.action();
}
}
输出结果:
ConcreteCommand1---execute
ConcreteReceiver1---doSomething
ConcreteCommand2---execute
ConcreteReceiver2---doSomething
优点
- 类间解藕
调用者角色与接受者角色之间没有任何依赖关系,调用者实现功能时只需要调用Command抽象类的execute方法就可以,不需要知道到底是哪个接收者执行。
- 可扩展性
Command子类可以非常容易的扩展,而调用者Invoker和高层次的模块Client不产生严重的代码藕合
- 命令模式结合其他模式会更优秀
命令模式与责任链模式,实现命令族解析任务。结合模板方法模式,则可以减少Command子类的膨胀问题
缺点
命令模式的缺点就是类膨胀,如果有N多子命令,那是不是Command子类就有N多。
Android源码中的模式实现
这个,我以android自带的camera为例,简单分析一下命令模式的使用,camera的源码(packages/apps/Camera2):
UML类图
具体实现代码
抽象命令类—CameraCommand:
public interface CameraCommand {
public void run() throws InterruptedException, CameraAccessException,
CameraCaptureSessionClosedException, ResourceAcquisitionFailedException;
}
我们可以看到此抽象命令类—CameraCommand非常简单,就是一个接口,定义了一个run()方法。
具体命令类
BurstCaptureCommand类,关键代码:
public class BurstCaptureCommand implements CameraCommand {
.......
private final Runnable mRestorePreviewCommand;
......
@Override
public void run() throws InterruptedException, CameraAccessException,
CameraCaptureSessionClosedException, ResourceAcquisitionFailedException {
......
mRestorePreviewCommand.run();
......
}
}
引命令接收者,我们可以看到是其它的一些具体的操作和一个接口:mRestorePreviewCommand。
PreviewCommand类,关键代码:
public class PreviewCommand implements CameraCommand {
private final FrameServer mFrameServer;
private final RequestBuilder.Factory mBuilderFactory;
private final int mRequestType;
......
public void run() throws InterruptedException, CameraAccessException,
CameraCaptureSessionClosedException, ResourceAcquisitionFailedException {
try (FrameServer.Session session = mFrameServer.createExclusiveSession()) {
RequestBuilder photoRequest = mBuilderFactory.create(mRequestType);
session.submitRequest(Arrays.asList(photoRequest.build()),
FrameServer.RequestType.REPEATING);
}
}
}
这个命令的接收者,我们可以在代码中看到是使用工厂模式的mBuilderFactory变量创建请求photoRequest ,再发送。
ZslPreviewCommand类,关键代码:
public class ZslPreviewCommand implements CameraCommand {
private final RequestBuilder.Factory mPreviewWarmupRequestBuilder;
......
private final RequestBuilder.Factory mZslRequestBuilder;
......
private final RequestBuilder.Factory mZslAndPreviewRequestBuilder;
......
public void run() throws InterruptedException, CameraAccessException,
CameraCaptureSessionClosedException, ResourceAcquisitionFailedException {
try (FrameServer.Session session = mFrameServer.createExclusiveSession()) {
if (mIsFirstRun.getAndSet(false)) {
if (ApiHelper.isLorLMr1() && ApiHelper.IS_NEXUS_6) {
List<Request> previewWarming = createWarmupBurst(mPreviewWarmupRequestBuilder,
mPreviewWarmupRequestType, 1);
session.submitRequest(previewWarming, RequestType.NON_REPEATING);
}
// Only run a warmup burst the first time this command is executed.
List<Request> zslWarmingBurst =
createWarmupBurst(mZslRequestBuilder, mZslRequestType, mWarmupBurstSize);
session.submitRequest(zslWarmingBurst, RequestType.NON_REPEATING);
}
// Build the zsl + preview repeating request.
RequestBuilder zslAndPreviewRequest = mZslAndPreviewRequestBuilder.create(
mZslAndPreviewRequestType);
List<Request> zslAndPreviewRepeating = Arrays.asList(zslAndPreviewRequest.build());
// Submit the normal repeating request.
session.submitRequest(zslAndPreviewRepeating, RequestType.REPEATING);
}
}
}
从上面的可以看到,其命令都是通过session.submitRequest来发送的。
FullAFScanCommand类,关键代码:
final class FullAFScanCommand implements CameraCommand {
.......
public void run() throws InterruptedException, CameraAccessException,
CameraCaptureSessionClosedException, ResourceAcquisitionFailedException {
FrameServer.Session session = mFrameServer.tryCreateExclusiveSession();
// Start a repeating sequence of idle requests
RequestBuilder idleBuilder = createAFIdleRequest(null);
session.submitRequest(Arrays.asList(idleBuilder.build()),
FrameServer.RequestType.REPEATING);
RequestBuilder cancelBuilder = createAFCancelRequest(null);
session.submitRequest(Arrays.asList(cancelBuilder.build()),
FrameServer.RequestType.NON_REPEATING);
// Start a repeating sequence of idle requests
idleBuilder = createAFIdleRequest(afScanResult);
session.submitRequest(Arrays.asList(idleBuilder.build()),
FrameServer.RequestType.REPEATING);
// Build a request to send a single AF_TRIGGER
RequestBuilder triggerBuilder = createAFTriggerRequest(afScanResult);
session.submitRequest(Arrays.asList(triggerBuilder.build()),
FrameServer.RequestType.NON_REPEATING);
......
}
}
我们可以看到此具体命令的实现,还是是上面的类一样,主要是通过 session.submitRequest发送请求来实现其具体操作的。
AFScanHoldResetCommand类,关键代码:
class AFScanHoldResetCommand implements CameraCommand {
private final CameraCommand mAFScanCommand;
private final ResettingDelayedExecutor mDelayedExecutor;
private final Runnable mPreviewRunnable;
public void run() throws CameraAccessException, InterruptedException,
CameraCaptureSessionClosedException, ResourceAcquisitionFailedException {
mDelayedExecutor.reset();
mAFScanCommand.run();
mDelayedExecutor.execute(new Runnable() {
public void run() {
// Reset metering regions and restart the preview.
mMeteringParametersUpdatable.update(GlobalMeteringParameters.create());
mPreviewRunnable.run();
}
});
}
}
我们可以看到其命令实现的逻辑主要是实现线程操作mPreviewRunnable.run()。
调用命令类—CameraCommandExecutor
调用命令类CameraCommandExecutor,作用相当于Invoker类,关键代码:
/**
* Executes camera commands on a thread pool.
*/
public class CameraCommandExecutor implements SafeCloseable {
//使用一个内部线程的方式来实现命令的调用
private class CommandRunnable implements Runnable {
private final CameraCommand mCommand;
public CommandRunnable(CameraCommand command) {
mCommand = command;
}
@Override
public void run() {
......
mCommand.run();
......
}
......
//这才是真正调用内部线程的命令的地方
/**
* Executes the given command, returning a Future to indicate its status and
* allow (interruptible) cancellation.
*/
public Future<?> execute(CameraCommand command) {
if (mClosed) {
return Futures.immediateFuture(null);
}
synchronized (mLock) {
if (mExecutor == null) {
// Create a new executor, if necessary.
mExecutor = mExecutorProvider.get();
}
checkNotNull(mExecutor);
return mExecutor.submit(new CommandRunnable(command));
}
}
......
命令接收者Receiver
我们通过以上的具体命令类,可以看到命令的接收者和具体的实现,其实这个例子没有使用严格的命令模式(如定义抽象命令接收者,具体命令接收者)来实现。
这主要原因是,有的命令接收实现非常简,可以直接用少量的代码或者使用一个线程就可以实现,不需要使用定义抽象类再使用具体类来实现,这样会导致类的数量比较多。
但是我们可以看到上面具体命令类中,有许多通过 session.submitRequest发送请求来实现其具体操作的。我们可以简单的分析一下:
接口中FrameServer 定义方法submitRequest:
public interface FrameServer {
public void submitRequest(List<Request> burstRequests, RequestType type)
throws CameraAccessException, InterruptedException,
CameraCaptureSessionClosedException, ResourceAcquisitionFailedException;
}
具体实现TagDispatchCaptureSession 类:
public class TagDispatchCaptureSession implements FrameServer.Session {
//具体实现:
public void submitRequest(List<Request> burstRequests, FrameServer.RequestType requestType)
throws
CameraAccessException, InterruptedException, CameraCaptureSessionClosedException,
ResourceAcquisitionFailedException {
try {
Map<Object, ResponseListener> tagListenerMap = new HashMap<Object, ResponseListener>();
List<CaptureRequest> captureRequests = new ArrayList<>(burstRequests.size());
for (Request request : burstRequests) {
Object tag = generateTag();
tagListenerMap.put(tag, request.getResponseListener());
CaptureRequestBuilderProxy builder = request.allocateCaptureRequest();
builder.setTag(tag);
captureRequests.add(builder.build());
}
if (requestType == FrameServer.RequestType.REPEATING) {
mCaptureSession.setRepeatingBurst(captureRequests, new
CaptureCallback(tagListenerMap), mCameraHandler);
} else {
mCaptureSession.captureBurst(captureRequests, new
CaptureCallback(tagListenerMap), mCameraHandler);
}
......
}
}
参考资料
(1).设计模式之禅—第15章 命令模式
(2)命令模式
https://github.com/simple-android-framework/android_design_patterns_analysis/tree/master/command/lijunhuayc