前言
在面向对象开发过程中,有时会遇到这样的问题,我们知道一个算法的关键步骤和执行顺序,但是,某些步骤的具体实现可能随着环境的变化而变化,这类问题的解决办法就可以用本篇的模板方法模式 (Template Pattern)来解决。
定义
定义一个操作中的算法的骨架,而将一些具体步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
使用场景
- 对于有特定执行步骤的方法。
- 重要、复杂的算法,可以把核心算法设计为模板方法,周边相关细节由各个子类实现。
UML 类图
- AbsTemplate: 抽象类,定义了一套算法框架
- ConcreteImplA: 具体实现类
- ConcreteImplB: 具体实现类
模板方法模式实现
这里以电脑开机为例,电脑开机过程大体有如下几步
- 上电
- 硬件检查
- 加载系统
- 登录
第一步:创建抽象类,定义算法框架
public abstract class AbstractComputer {
public void powerOn() {
System.out.println("开启电源");
}
public void checkHardWare() {
System.out.println("硬件检查");
}
public void loadOS() {
System.out.println("加载操作系统");
}
public void login() {
System.out.println("直接进入系统");
}
public final void startUp() {
// 开机步骤
powerOn();
checkHardWare();
loadOS();
login();
}
}
startUp方法为final
修饰,防止算法框架被修改。
第二步:定义具体实现类
public class CoderComputer extends AbstractComputer {
@Override
public void login() {
System.out.println("用户及密码验证,验证正确进入系统");
}
}
程序员的电脑开机时在登录阶段需要验证密码,故重写 login
方法
public class MilitaryComputer extends AbstractComputer {
@Override
public void checkHardWare() {
super.checkHardWare();
System.out.println("检查硬件防火墙");
}
@Override
public void login() {
System.out.println("双层密码校验,验证正确进入系统");
}
}
军用电脑,检查硬件阶段还要进行硬件防火墙检查,且登录阶段需要双层密码校验。
3. 客户端调用
public class Client {
public static void main(String[] args) {
AbstractComputer computer = new CoderComputer();
computer.startUp();
computer = new MilitaryComputer();
computer.startUp();
}
}
输出结果如下:
开启电源
硬件检查
加载操作系统
用户及密码验证,验证正确进入系统
开启电源
硬件检查
检查硬件防火墙
加载操作系统
双层密码校验,验证正确进入系统
startUp 方法中的算法步骤我们可以称为一个套路,也就是模板方法。
注意:为防止模板被篡改,通常模板方法都会用 final 关键字修饰
总结:
模板方法优点:
1.封装不变部分,扩展可变部分
2.行为由父类控制,子类实现
模板方法缺点:
每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大
Android 源码中的模板方法模式
1. Activity生命周期
Activity生命周期都比较熟悉,方法有onCreate,onStart,onResume ... 等,作为开发者,只需关注Activity 的某个状态,而无需关心具体的 Activity创建流程核心逻辑,显然需要使用模板方法模式。
Activity的创建流程核心都是通过ActivityThread完成的,其负责与 AMS 交互并在合适的时机调用 Activity的生命周期。
ActivityThread.java
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
Activity a = performLaunchActivity(r, customIntent);
}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
// 最终调用 Activity 的 onCreate 方法
mInstrumentation.callActivityOnCreate(activity, r.state);
...
// 内部会调用 mInstrumentation.callActivityOnStart(this); 最终调用 Activity 的 onStart 方法
activity.performStart();
}
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
...
r = performResumeActivity(token, clearHide, reason);
...
}
public final ActivityClientRecord performResumeActivity(IBinder token,
boolean clearHide, String reason) {
...
// 内部会调用 mInstrumentation.callActivityOnResume(this), 最终调用 Activity 的 onResume 方法
r.activity.performResume();
...
}
Android 系统真正启动Activity的方法就是 handleLaunchActivity
方法,该方法中会进行启动 Activity 一些必须的工作,最后都通过 Instrumentation 的对应方法调用到 Activity 的生命周期方法。
- ActivityThread 的 handleLaunchActivity 就是模板方法,规范了生命周期的顺序
- 具体的 Activity 就是实现类
2. AsyncTask
AsyncTask 异步任务使用时调用 execute 方法,方法调用顺序如下:
- onPreExecute: 运行在 UI 线程,用于任务前的准备工作
- doInBackground:运行在子线程,用于实际耗时任务的处理
- onPostExecute:运行在UI线程,用于获取最终任务处理的结果
很明显对于存在先后顺序逻辑的用模板方法模式再合适不过了,让我们看看 AsyncTask 源码一探究竟。
AsyncTask.java
public abstract class AsyncTask {
// 1. final 修饰,其实该方法就是模板方法
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask executeOnExecutor(Executor exec,
Params... params) {
...
// 2. 执行 onPreExecute
onPreExecute();
// 3. 传递给子线程的参数
mWorker.mParams = params;
// 4. Executor 执行mFuture
exec.execute(mFuture);
return this;
}
}
这里已经可以看出模板方法了,且已经调用了 onPreExecute 方法,这里我们继续看mFuture 和 mWorker 又是什么,这两个对象在 AsyncTask 的构造方法中进行了初始化
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
// Worker对象的构建
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 1. 执行 doInBackground 方法
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
// 2. 执行postResult 方法并携带执行结果参数
postResult(result);
}
return result;
}
};
// 构建 FutureTask 对象,mWorker 作为参数
mFuture = new FutureTask(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} ...
}
};
}
当exec.execute(mFuture)
执行后,mFuture 的 run
方法会被执行,此时运行在子线程,在其内部会调用 mWorker 的 call
方法,进而调用了 doInBackground 方法,然后调用了 postResult 方法,我们接着看该方法。
private Result postResult(Result result) {
// 1. 发送 MESSAGE_POST_RESULT 消息
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
private static class InternalHandler extends Handler {
...
@Override
public void handleMessage(Message msg) {
AsyncTaskResult> result = (AsyncTaskResult>) msg.obj;
switch (msg.what) {
// 2. 处理 MESSAGE_POST_RESULT 消息
case MESSAGE_POST_RESULT:
// 该 mTask 就是AsyncTask 对象
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
// 3. onPostExecute 执行
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
postResult(result)
方法内部是通过发送 MESSAGE_POST_RESULT
消息, 进而交由InternalHandler 处理,此时运行在 UI线程,执行result.mTask.finish(result.mData[0]);
,也就是调用 AsyncTask 的 finish 方法;finish 方法中会调用 onPostExecute。
这样AsyncTask 就完成异步线程的处理并返回结果。