命令模式以及在android中的使用

本文详细介绍了命令模式的原理,通过一个教学管理的例子展示了如何将调用者与执行者解耦。在Android中,命令模式的变种如PackageManagerService的HandlerParams和Executor的execute方法也体现了这一模式,它们将任务封装在内部类或Runnable中,实现了任务的异步执行。这种方式有利于代码的拓展和维护。
摘要由CSDN通过智能技术生成

一、原理

命令模式将任务请求封装成命令对象,命令对象中封装有任务处理者对象,调用者持有这个命令对象,这就使得调用者和处理者没有直接联系,降低了耦合度。同时,根据不同的请求可以参数化命令对象,可以将多个对象放入到集合中排队执行。命令模式的UML图如下,安利一个UML绘制网站https://app.diagrams.net/。
在这里插入图片描述
命令模式可以适用组合的方式将发出请求的对象和执行请求的对象解耦,这样后面拓展新的命令比较容易;缺点就是会出现过多的具体命令类以及处理者类。

二、实践

那上课为例吧,现在有个一个调用者-教学主任(TeachingDirector)、不同的课程(命令对象TeachChineseCommand、TeachEnglishCommand、TeachMathCommand)以及授课老师(处理者):语文老师(ChineseTeacher)、数学老师(MathTeacher)、英语老师(EnglishTeacher)。规则就是教导主任可以发布上课指令去触发上课命令对象,然后上课命令对象调用具体的老师去执行相应的教学任务。
在这里插入图片描述
ChineseTeacher.java

class ChineseTeacher {
    public void action() {
        LogUtil.d("teach Chinese");
    }
}

EnglishTeacher.java

class EnglishTeacher {
    public void action() {
        LogUtil.d("teach English");
    }
}

MathTeacher.java

class MathTeacher {
    public void action() {
        LogUtil.d("teach Math");
    }
}

Command.java

public interface Command {
    void execute();
}

TeachChineseCommand.java

class TeachChineseCommand implements Command{

    private ChineseTeacher chineseTeacher;

    public TeachChineseCommand(ChineseTeacher chineseTeacher) {
        this.chineseTeacher = chineseTeacher;
    }

    @Override
    public void execute() {
        if (chineseTeacher!=null) {
            chineseTeacher.action();
        }
    }
}

TeachEnglishCommand.java

class TeachEnglishCommand implements Command{

    private EnglishTeacher englishTeacher;

    public TeachEnglishCommand(EnglishTeacher englishTeacher) {
        this.englishTeacher = englishTeacher;
    }

    @Override
    public void execute() {
        if (englishTeacher!=null) {
            englishTeacher.action();
        }
    }
}

TeachMathCommand.java

class TeachMathCommand implements Command{

    private MathTeacher mathTeacher;

    public TeachMathCommand(MathTeacher mathTeacher) {
        this.mathTeacher = mathTeacher;
    }

    @Override
    public void execute() {
        if (mathTeacher!=null) {
            mathTeacher.action();
        }
    }
}

TeachingDirector.java

public class TeachingDirector {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void doAction() {
        if (this.command !=null) {
            this.command.execute();
        }
    }
}

测试代码如下:

private void doTest() {
    TeachChineseCommand teachChineseCommand = new TeachChineseCommand(new ChineseTeacher());
    TeachEnglishCommand teachEnglishCommand = new TeachEnglishCommand(new EnglishTeacher());
    TeachMathCommand teachMathCommand = new TeachMathCommand(new MathTeacher());
    TeachingDirector teachingDirector = new TeachingDirector();
    teachingDirector.setCommand(teachChineseCommand);
    teachingDirector.doAction();

    teachingDirector.setCommand(teachEnglishCommand);
    teachingDirector.doAction();

    teachingDirector.setCommand(teachMathCommand);
    teachingDirector.doAction();
}

log打印如下:

ChineseTeacher.action(L:8): teach Chinese
EnglishTeacher.action(L:8): teach English
MathTeacher.action(L:8): teach Math

此时,教学主任和学科老师没有直接联系,教学主任只需发布具体的上课命令,上课命令会通知指定的学科老师上课,达到解耦的目的。后续如果有其它学科老师以及相应的教学命令对象可以直接添加然后调用教学主任类的setCommand即可,对固有代码修改小,拓展性强。

三、android中命令模式的使用

android中适用的更多的是命令模式的变种-命令对象不再设置接收者,命令对象本身就完成了具体任务。

3.1 PackageManagerService.HandlerParams

在PackageManagerService.java中有一个HandlerParams抽象内部类,HandlerParams的子类InstallParams、MultiPackageInstallParams重写了父类的抽象方法handleStartCopy()和handleReturnCode(),startCopy()会使用模板方法模式调用handleStartCopy()、handleReturnCode()方法。

//PackageManagerService.java
private abstract class HandlerParams {
    /** User handle for the user requesting the information or installation. */
    private final UserHandle mUser;
    String traceMethod;
    int traceCookie;

    HandlerParams(UserHandle user) {
        mUser = user;
    }

    UserHandle getUser() {
        return mUser;
    }

    /**
     * Gets the user handle for the user that the rollback agent should
     * use to look up information about this installation when enabling
     * rollback.
     */
    UserHandle getRollbackUser() {
        // The session for packages installed for "all" users is
        // associated with the "system" user.
        if (mUser == UserHandle.ALL) {
            return UserHandle.SYSTEM;
        }
        return mUser;
    }

    HandlerParams setTraceMethod(String traceMethod) {
        this.traceMethod = traceMethod;
        return this;
    }

    HandlerParams setTraceCookie(int traceCookie) {
        this.traceCookie = traceCookie;
        return this;
    }

    final void startCopy() { //模板方法模式
        if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
        handleStartCopy();
        handleReturnCode();
    }

    abstract void handleStartCopy();
    abstract void handleReturnCode();
}

在installStage方法中就会将InstallParams 作为参数传入到mHandler中,mHandler是PackageHandler实例。然后在PackageHandler的doHandleMessage方法中去处理消息,当msg.what是INIT_COPY时取出的msg.obj就是InstallParams 对象,并调用InstallParams的startCopy()方法。

//PackageManagerService.java
void installStage(ActiveInstallSession activeInstallSession) {
    if (DEBUG_INSTANT) {
        if ((activeInstallSession.getSessionParams().installFlags
                & PackageManager.INSTALL_INSTANT_APP) != 0) {
            Slog.d(TAG, "Ephemeral install of " + activeInstallSession.getPackageName());
        }
    }
    final Message msg = mHandler.obtainMessage(INIT_COPY);//msg.what
    final InstallParams params = new InstallParams(activeInstallSession);
    params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
    msg.obj = params;

    Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
            System.identityHashCode(msg.obj));
    Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
            System.identityHashCode(msg.obj));

    mHandler.sendMessage(msg);
}
……
class PackageHandler extends Handler {

    PackageHandler(Looper looper) {
        super(looper);
    }

    public void handleMessage(Message msg) {
        try {
            doHandleMessage(msg);
        } finally {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        }
    }

    void doHandleMessage(Message msg) {
        switch (msg.what) {
            case INIT_COPY: {
                HandlerParams params = (HandlerParams) msg.obj;
                if (params != null) {
                    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
                    Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                            System.identityHashCode(params));
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
                    params.startCopy();
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }
                break;
            }
			……
		}
	}
}

这里的PackageManagerService.java中InstallParams就相当于一个命令对象,其自身实现了具体任务startCopy。

3.2 executor.execute

java中有线程池,线程池对象可以执行execute方法,execute方法参数就是一个Runnable指令,此时就可以创建一个Runnable命令对象,然后通过线程池 去触发对应的操作。

Executor executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
    @Override
    public void run() {
        LogUtil.d("do yourself in Runnable");
    }
});
Thread thread = new Thread(){
    @Override
    public void run() {
        super.run();
        LogUtil.d("do yourself in Thread");
    }
};
executor.execute(thread);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值