设计模式之命令模式---Command Pattern

模式的定义

命令模式是一个高内聚的模式,定义如下:

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

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hfreeman2008

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值