您的位置:首页 > 其它

浅析设计模式之模板模式

2015-04-18 22:32 183 查看

浅析设计模式之模板模式

定义

In software engineering, the template method pattern is a behavioral design pattern that defines the program skeleton of an algorithm in a method, called template method, which defers some steps to subclasses. It lets one redefine certain steps of an algorithm without changing the algorithm’s structure.

软件工程中,模板模式是活动型设计模式,在一个方法中定义了程序的轮廓结构,这个方法成为模板方法,将一些实现交给子类进行处理。这使程序员自己处理需要变化的算法,但并不会改变算法的整体结构

对模板模式的理解

模板模式主要通过一个模板方法来进行实现。这个方法已经有了执行的具体步骤,即算法轮廓。但是由于有的步骤中是由差异的,那么可以将这些步骤抽取出来为一个抽象的方法,让子类进行具体实现。

模板模式适合定义一群具有相同步骤类的父类,因为这样可以在新建子类时而不同重复以前已经存在的代码了,实现了代码的复用,同时也确保了代码结构的准确性

模式的使用场景

多个子类有公有的方法,并且逻辑基本相同时。

重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。

重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数约束其行为。

uml图



角色分析

AbstractClass: 封装了算法结构,将变化抽象,将不变的实例

ConcreteClass: 实现抽象类,将变化进行实现

Hook, 抽象父类的实例方法,可以被子类覆盖

Hook的分析

Hook是个实例方法,是程序员可选实现的方法,通常在父类是空的

如果父类实例方法不能被覆盖,需要使用private标记符,以此是它不可更改,可以在模板方法加入final前缀,使其不能被子类覆盖

模板方法命名规则

In order to identify the primitive methods is it better to use a specific naming convention. For example the prefix “do” can be used for primitive methods. In a similar way the customizations hooks can have prefixes like “pre” and “post”.

可以使用do前缀标记这个方法是需要实现的抽象方法,用pre或者post标记这个方法是个hook

模板模式与策略模式的区别

策略模式封装了各个同种类型的算法类

模板模式定义了模板方法的结构,使需要实现的方法抽象出来供子类实现

在使用中,策略模式使用的是代理,而模板模式使用的是继承

项目中使用过的代码

public abstract class BaseActivity extends Activity
implements OnHeaderClickListener, OnClickListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());

autoInitView();
initHeaderView();
initComponents();
}

public abstract void initHeaderView();

public abstract void initComponents();

public void autoInitView() {
AutoViewInitializer.newInstance(this).startInit();
}

public abstract int getLayoutId();

@Override
public abstract void onClick(View v);

@Override
public abstract void OnHeaderClick(View v, int option);

}


Android源码中的模式实现

在Android中,使用了模板方法且为我们熟知的一个典型类就是AsyncTask了,关于AsyncTask的更详细的分析请移步Android中AsyncTask的使用与源码分析,我们这里只分析在该类中使用的模板方法模式。

在使用AsyncTask时,我们都有知道耗时的方法要放在doInBackground(Params… params)中,在doInBackground之前如果还想做一些类似初始化的操作可以写在onPreExecute方法中,当doInBackground方法执行完成后,会执行onPostExecute方法,而我们只需要构建AsyncTask对象,然后执行execute方法即可。我们可以看到,它整个执行过程其实是一个框架,具体的实现都需要子类来完成。而且它执行的算法框架是固定的,调用execute后会依次执行onPreExecute,doInBackground,onPostExecute,当然你也可以通过onProgressUpdate来更新进度。我们可以简单的理解为如下图的模式 :



下面我们看源码,首先我们看执行异步任务的入口, 即execute方法 :

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}

mStatus = Status.RUNNING;

onPreExecute();

mWorker.mParams = params;
exec.execute(mFuture);

return this;
}


可以看到execute方法(为final类型的方法)调用了executeOnExecutor方法,在该方法中会判断该任务的状态,如果不是PENDING状态则抛出异常,这也解释了为什么AsyncTask只能被执行一次,因此如果该任务已经被执行过的话那么它的状态就会变成FINISHED。继续往下看,我们看到在executeOnExecutor方法中首先执行了onPreExecute方法,并且该方法执行在UI线程。然后将params参数传递给了mWorker对象的mParams字段,然后执行了exec.execute(mFuture)方法。

mWorker和mFuture又是什么呢?其实mWorker只是实现了Callable接口,并添加了一个参数数组字段,关于Callable和FutureTask的资料请参考Java中的Runnable、Callable、Future、FutureTask的区别与示例,我们挨个来分析吧,跟踪代码我们可以看到,这两个字段都是在构造函数中初始化。

public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return postResult(doInBackground(mParams));
}
};

mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
final Result result = get();

postResultIfNotInvoked(result);
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
} catch (Throwable t) {
throw new RuntimeException("An error occured while executing "
+ "doInBackground()", t);
}
}
};
}


简单的说就是mFuture就包装了这个mWorker对象,会调用mWorker对象的call方法,并且将之返回给调用者。

关于AsyncTask的更详细的分析请移步Android中AsyncTask的使用与源码分析,我们这里只分析模板方法模式。总之,call方法会在子线程中调用,而在call方法中又调用了doInBackground方法,因此doInBackground会执行在子线程。doInBackground会返回结果,最终通过postResult投递给UI线程。

我们再看看postResult的实现 :

private Result postResult(Result result) {
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}

private static class InternalHandler extends Handler {
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
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 {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}


可以看到,postResult就是把一个消息( msg.what == MESSAGE_POST_RESULT)发送给sHandler,sHandler类型为InternalHandler类型,当InternalHandler接到MESSAGE_POST_RESULT类型的消息时就会调用result.mTask.finish(result.mData[0])方法。我们可以看到result为AsyncTaskResult类型,源码如下 :

@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;

AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}


可以看到mTask就是AsyncTask对象,调用AsyncTask对象的finish方法时又调用了onPostExecute,这个时候整个执行过程就完成了。

总之,execute方法内部封装了onPreExecute, doInBackground, onPostExecute这个算法框架,用户可以根据自己的需求来在覆写这几个方法,使得用户可以很方便的使用异步任务来完成耗时操作,又可以通过onPostExecute来完成更新UI线程的工作。

另一个比较好的模板方法示例就是Activity的声明周期函数,例如Activity从onCreate、onStart、onResume这些程式化的执行模板,这就是一个Activity的模板方法。

优点

封装不变部分,扩展可变部分,减少重复代码量

提取公共部分代码,便于维护

缺点

模板方法会带来代码阅读的难度,会让心觉得难以理解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  设计模式