一、原理
命令模式将任务请求封装成命令对象,命令对象中封装有任务处理者对象,调用者持有这个命令对象,这就使得调用者和处理者没有直接联系,降低了耦合度。同时,根据不同的请求可以参数化命令对象,可以将多个对象放入到集合中排队执行。命令模式的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()方法。
private abstract class HandlerParams {
private final UserHandle mUser;
String traceMethod;
int traceCookie;
HandlerParams(UserHandle user) {
mUser = user;
}
UserHandle getUser() {
return mUser;
}
UserHandle getRollbackUser() {
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()方法。
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);
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);
|