1.模板方法模式
模板方法模式是一种行为型设计模式。它定义一个算法骨架,而将一些步骤延迟到子类中,即在父类中编排主流程,将步骤实现延迟到子类去实现。模板方法使得子类可以在不改变算法的结构下,重定义该算法的某些特定步骤。
此模式总结概括就是流程封装,也就是把某个固定的流程封装到一个final方法中,并且让子类能够定制这个流程中的某些或所有步骤,这就要求父类提取公用的代码,提升代码的复用率,同时带来了更好的可扩展性。
UML类图:
抽象父类AbstractClass:实现了模板方法,定义了一套算法框架。
具体实现类ConcreteClass1、ConcreteClass2:实现抽象类中的抽象方法,即不同的对象的具体实现细节。
使用场景:
①算法的整体步骤很固定,但其中个别部分易变时,可使用模板方法模式将容易变化的部分抽象出来,供子类去实现。
②当多个子类有公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。
当需要控制子类的扩展时,模板方法模式在特定点调用钩子方法,使用钩子方法让子类决定父类的某个步骤是否执行,实现子类对父类的反向控制。
②重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。
③重构时,模板方法模式是一个经常使用的模式,把相同的代码抽到父类中,然后通过子类约束其行为。
模板方法模式优点:
①提高代码复用性,将相同部分的代码放在抽象的父类中,而不同的代码放入不同的子类中。
②实现了反向控制一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制,符合开闭原则。
缺点:
①每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
2.模板方法模式的实现
抽象模板:
public abstract class AbstractTemplate {
//模板方法
public void templateMethod() {
doOperation1();
if(isNeedOperation2) {
doOperation2();
}
doOperation3();
}
// 基本方法--抽象方法
protected abstract void doOperation1();
// 基本方法
protected void doOperation2() {
// 空实现,子类实现
}
// 基本方法-具体方法由抽象类声明并实现,而子类并不实现或置换
protected void doOperation3() {
// 业务逻辑省略..
}
//钩子方法,是否需要第二步,默认需要
protected boolean isNeedOperation2(){
return true; //使用钩子方法让子类决定父类的某个步骤是否执行,实现子类对父类的反向控制
}
}
具体模板:
public class ConcreteTemplateA extends AbstractTemplate {
@Override
protected void doOperation1() {
// 业务逻辑省略..
}
@Override
protected void doOperation2() {
// 业务逻辑省略..
}
protected boolean isNeedOperation2(){
return true;
}
}
public class ConcreteTemplateB extends AbstractTemplate {
@Override
protected void doOperation1() {
// 业务逻辑省略..
}
@Override
protected void doOperation2() {
// 业务逻辑省略..
}
protected boolean isNeedOperation2(){
return false; //该子类的算法不需要第二步
}
}
客户端:
ConcreteTemplateA aa = new ConcreteTemplateA();
ConcreteTemplateB bb = new ConcreteTemplateB();
aa.templateMethod();
bb.templateMethod();
在Android开发中,每一个activity都会有很多相同的方法,例如初始化界面、初始化数据、设置相同的主题等。这时候就可以使用模板方法模式来优化。
①新建一个BaseActivity基类,在里面定义算法框架
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
//模板方法,多个基本方法的组合
public final void firstInit{
setContentView(initLayout());
initView();
if(isInitData()) { //钩子方法
initData();
}
}
//基本方法,模板方法里面调用
protected abstract int initLayout();
protected abstract void initData();
protected abstract void initView();
//钩子方法,是否要初始化数据,默认为true
protected boolean isInitData() {
return true;
}
}
把setContentView()方法放在了父类中,在每一个子类传入布局id就可以了。
定义了一个钩子方法,当默认需要初始化数据时,什么都不用改动;如果不需要初始化数据,只需要覆写钩子方法isInitData并返回false即可。
②子类
public class TestActivity extends BaseActivity {
@Override
protected int initLayout() {
return R.layout.activity_test;
}
@Override
protected void initView() {
//本activity的findViewById()等操作
}
@Override
protected void initData() {
//本activity的初始化数据
}
}
模板方法实际上是封装一个固定流程,就像一套固定模板一样,第一步该做什么,第二步该做什么都已经在抽象类中定义好。而子类可以有不同的算法实现,在框架不被修改的情况下实现某些步骤的算法替换。
注意:
1)父类中的基本方法尽量设计为protected,符合迪米特原则。
2)父类中的模板方法一般设置为final,不允许子类重写。目的一是为了避免子类恶意操作,二是为了模板的共性。
3.Android源码中的模板方法模式
①AsyncTask
在使用AsyncTask时,把耗时操作放到doInBackground(Params… params)中,在doInBackground之前,如果想做一些初始化操作,可以把实现写在onPreExecute中,当doInBackground执行完后会执行onPostExecute方法,而我们只需要构建AsyncTask对象,然后执行execute方法。
②Activity的生命周期
ActivityThread的main函数被调用后,依次执行Activity的onCreate、onStart、onResume函数,用户通常在Activity的子类中覆写onCreate方法,并且在该方法中调用setContentView来设置布局。