概述
模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。
认识模板方法
选自Head First一个例子,很好理解该模式。
需求:咖啡店有两个饮料冲泡法,分别为咖啡冲泡法、茶叶冲泡法。
咖啡冲泡法:
(1)把水煮沸
(2)用沸水冲泡咖啡
(3)把咖啡倒进杯子
(4)加糖和牛奶
public class Coffee
void prepareRecipe(){
boilWater();
brewCoffeeGrinds();
PourIncup();
addSugarAndMilk();
}
//具体实现……
茶叶冲泡法:
(1)把水煮沸
(2)用沸水浸泡茶叶
(3)把茶倒进杯子
(4)加柠檬
public class Tea
void prepareRecipe(){
boilWater();
steepTeaBag();
PourIncup();
addLemon();
}
//具体实现……
我们注意到两份冲泡法都采用了相同的算法
:
(1)把水煮沸
(2)用热水泡咖啡或茶
(3)把饮料倒进杯子
(4)在饮料内加入适当的调料
我们可以在超类中抽象出来prepareRecipe方法,如下:
public abstract class CaffeineBeverage
{
final void prepareRecipe(){
boilWater();
brew();
pourIncup();
addCondiments();
}
abstract void brew();
abstract void addCondiments();
void boilWater(){
System.out.println("Boiling water");
}
voud pourInCup(){
System.out.println("Pouring into cup");
}
}
子类实现步骤:
public class Tea extends CaffenieBeverage{
public void brew(){
System.out.println("Steeping the tea");
}
public void addCondiments(){
System.out.println("Add8hg Lemon");
}
}
定义
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
这个模式用来创建一个算法的模板,模板就是一个方法,这个方法将算法定义成一组步骤
,其中的任何步骤都可以是抽象
的,由子类负责实现
。这确保算法的结构保持不变
,同时又子类提供部分实现。
模板方法进阶
abstract class AbstractClass{
final void templateMethod(){
primitiveOperation1();
primitiveOperation2();
concreteOperation();
hook();
}
abstract void primitiveOperation1();
abstract void primitiveOperation2();
final void concreteOperation(){
//实现
}
void hook(){
//啥都不做,钩子
}
}
其中钩子是不同于上面的普通模板方法,钩子可以让用户决定
要不要覆盖方法。
钩子用法之——控制流程
如:
public abstract class CaffeineBeverageWithHook
{
final void prepareRecipe(){
boilWater();
brew();
pourIncup();
//见下(1)
if(customerWantsCondiments()){
addCondiments();
}
}
abstract void brew();
abstract void addCondiments();
void boilWater(){
System.out.println("Boiling water");
}
voud pourInCup(){
System.out.println("Pouring into cup");
}
//见下(2)
boolean customerWantsCondiments(){
return true;
}
}
注意区别:
(1)我们加上了一个小小的条件语句,而该条件是否成立,由一个具体方法customerWantsCondiments()决定。
(2)我们在下面定义了一个方法,是空的缺省实现。这个方法只返回True。这就是一个钩子,子类可以覆盖这个方法,但不见得一定要这么做。
钩子的其他用法
让子类能够有机会对模板方法中某些即将发生的(或刚刚发生的)步骤作出反应。
比如:
名为justReOrderedList()的钩子方法允许子类在内部列表重新组织后执行某些动作(例如在屏幕上重新显示数据)。
举例
Android中的Activity的生命周期控制函数,如onCreate,onStop,onResume,onDestroy
方法等,它们都属于钩子
,钩子默认不进行任何操作。我们可以通过覆写这些方法,进行我们自己的业务逻辑。
好莱坞原则
别调用我们,我们会调用你。
好莱坞原则可以给我们一钟防止”依赖腐败”的方法。当高层组件依赖底层组件,而底层组件又依赖高层组件,而高层组件又依赖边侧组件,而边侧组件又依赖底层组件时,依赖腐败就发生了。
好莱坞原则就是高层组件对待底层组件的方式是别调用我们,我们会调用你
。
JAVA API中的模板方法
如:Arrays.sort(Object[] arrays)
方法,它实现了对一个对象数组进行排序,大致源码如下:
public static void sort(Object[] a){
Object aux[] = (Object[] )a.clone();
mergeSort(aux,a,0,a.length,0);
}
public static void mergeSort(Object[] src[],Object dest[],int low,int high,int off){
for(int i = low + off ; i < high ; i++){
for(int j = i ; j > low &&((Comparable)dest[j-1].compareTo((Comparable)dest[j]>0);j--){
swap(dest,j,j-1);
}
}
return;
}
其中sort是暴露出来的静态方法,实则调用了mergeSort()
方法,这就是一个模板方法,它规定了一个算法,但sort方法需要知道排序的规则(即知道两个对象谁大谁小),所以该对象必须实现一个接口——Comparable
。
如下:
public class person implements Comparable{
String name;
int age;
//setter getter ....
public int compareTo(Object object){
Person p = (Person) object;
if(this.age < p.age){
return -1;
}
else if(this.age == p.age){
return 0;
}
else{
return 1;
}
}
}
Person实现了Comparable
接口,则可以进行用compareTo
方法进行比较大小,故可以使用Arrays.sort()
方法进行排序。
该方法是一种简单的模板方法,模板方法并不一定都需要子类去继承或实现算法,模板方法只是提供一个算法骨架
。sort方法希望能够适用于所有的数组,所以它们定义了一个静态方法,而由被排序的对象内的每个元素自行提供比较大小的算法部分
,它的实现符合模板方法的精神。
其他的模板方法:InputStream中的read方法,它是由子类实现的,而这个方法又会被read(byte b[],int off,int len)模板方法使用。