一、举个栗子
用Java实现一下吧
//煮咖啡 //泡茶
public class Coffee { public class Tea {
void prepareRecipe() { void prepareRecipe() {
boilWater(); boilWater();
brewCoffeeGrinds(); steepTeaBag();
pourInCup(); pourInCup();
addSugarAndMilk(); addLemon();
} }
//每个操作步骤的具体实现 //每个操作步骤的具体实现
public void boilWater() { public void boilWater() {
.... ....
} }
public void brewCoffeeGrinds() { public void steepTeaBag() {
.... ....
} }
public void pourInCup() { public void pourInCup() {
.... ....
} }
public void addSugarAndMilk() { public void addLemon() {
.... ....
} }
} }
可以看出有很多重复的代码,那么直接把重复的代码抽取出来
1.版本1.0
2.版本2.0
再仔细看看,还是有重复的方法
浸泡(steep)和冲泡(brew)差异其实并不大,就都叫brew(),而加糖,牛奶和柠檬也很相似,都是加入调料,都叫addCondiments()好了。
有了新的prepareRecipe(),就可以设计咖啡因饮料(CaffeineBeverage)的超类了
A.设计超类
public abstract class CaffeineBeverage{
//声明为final,保证子类不会覆盖他
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}
//下面这两个类必须是抽象的,因为咖啡和茶的处理方式不同
abstract void brew();
abstract void addCondiments();
//下面这两个类是共同的,放到超类里实现
public void boilWater() {
....
}
public void pourInCup() {
....
}
}
B.咖啡类的实现
public class Coffee extends CaffeineBeverage {
public void brew() {
...
}
public void addCondiments() {
...
}
}
```
C.茶类的实现
```
public class Tea extends CaffeineBeverage {
public void brew() {
...
}
public void addCondiments() {
...
}
}
主要思想
二、模板方法
-
定义
模板方法:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
冲泡咖啡和茶的例子就是一个模板方法
通过这个例子可以看出,由CaffineBeverage类主导一切,拥有算法,并通过final保护这个算法,而不是Coffee或Tea各自有各自的算法,并且可以使代码进行复用,所有的算法只存在于CaffineBeverage类中,修改也容易,不必修改多个类,如果新加入其它咖啡因的饮料也非常容易。
2、类图
3、分析抽象类
//这个抽象类作为基类,其子类必须实现其操作
abstract class AbstractClass {
//声明为final,以免子类改变这个算法的顺序
final void templatemethod() {
//定义了一连串的步骤,每个步骤由一个方法代表
primitiveOperation1();
primitiveOperation2();
concreteOperation();
hook();
}
//下面的抽象方法子类必须实现
abstract void primitiveOperation1();
abstract void primitiveOperation2();
//声明为final,使得子类无法覆盖它,它可以被模板方法直接使用,或被子类使用
final void concreteOperation() {
...
}
//可以有“默认不做的方法”,这个方法为“hook”,子类可视情况决定是否覆盖
void hook() {}
}
4、hook()
hook()是一种被声明在抽象类中的方法,但只有空的或默认的实现,hook()的存在,可以让子类有能力对算法的不同点进行挂钩,要不要挂钩,由子类决定
举例:
public abstract class CaffeineBeverageWithHook {
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
//加上一个条件,如果顾客“想要”调料,才加入
if (customerWantsCondiments()) {
addCondiments();
}
}
abstract void brew();
abstract void addCondiments();
public void boilWater() {
....
}
public void pourInCup() {
....
}
//定义一个方法,通常是空的缺省实现,只会返回true
boolean customerWantsCondiments() {
//子类可以覆盖
return true;
}
}
使用hook
public class CoffeeWithHook extends CaffeineBeverageWithHook {
public void brew() {
...
}
public void addCondiments() {
...
}
//子类覆盖了这个hook,提供了自己的功能
public boolean customerWantsCondiments(String answer) {
//让用户输入他们对调料的决定,根据用户的输入返回true或false
String answer = answer;
if (answer.equals(“Yes”)) {
return true;
} else {
return false;
}
}
}
当子类“必须”提供算法中某个方法或步骤实现时,就使用抽象方法,如果算法的某个部分是可选的,就用hook,这样子类可以去选择是否实现它。hook的一个用法是像上面的例子,使抽象类中的某些方法是可选的,另一种方法是让子类能够有机会对模板方法中某些即将发生(或刚刚发生的)步骤做出反应,例如,允许子类在得到数据后执行某些动作(显示数据等)。
5、好莱坞原则
别调用我们,我们会调用你--允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些低层组件。
三、模板方法在Android中的应用
1、Activity、Fragment
Activity、Fragment等有自己固定的生命周期,它按照Android自己设计的状态进行流转,但是在那个状态需要做什么事情,是与具体的应用有关。因此Activity、Fragment就定义了许多hook方法,如onStart(),onResume(),onStop()等,应用要想处理自己的业务,就继承Activity或者Fragment,并重写这些方法就可以了。
2、View
public class View{
//钩子方法,空实现
protected void onDraw(Canvas canvas) {
}
//钩子方法,空实现
protected void dispatchDraw(Canvas canvas) {
}
//绘制方法,定义绘制流程
public void draw(Canvas canvas) {
}
}
3、AsyncTask
在使用AsyncTask时,需要把耗时操作放到doInBackground(Params… params)中,在doInBackground之前,如果想做一些初始化操作,可以把实现写在onPreExecute中,当doInBackground执行完后会执行onPostExecute方法,而我们只需要构建AsyncTask对象,然后执行execute方法。
private AsyncTask task = new AsyncTask() {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Object doInBackGround(Object[] params) {
return null;
}
@Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
}
}