基本定义
定义算法步骤,将实现延迟到子类
在一个方法中定义算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤
案例
问题:有些人没有咖啡就活不下去,有些人则离不开茶。两者除了共同的成分咖啡因相同外。他们的冲泡方式也非常的相似
一般做法(错误案例)
Coffee
public class Coffee {
void prepareRecipe() {// 将每一个步骤给分离
boilWater();
brewCoffeeGrinds();
pourInCup();
addSugarAndMilk();
}
// 实现算法的每一个步骤:煮沸水 冲泡咖啡 把咖啡倒进杯子 加糖和加奶
private void boilWater() {
System.out.println("Boiling water");
}
private void brewCoffeeGrinds() {
System.out.println("Dripping Coffee through filter");
}
private void pourInCup() {
System.out.println("Pouring into cup");
}
private void addSugarAndMilk() {
System.out.println("Adding Suger and milk");
}
}
Tea
public class Coffee {
void prepareRecipe() {// 将每一个步骤给分离
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}
// 实现算法的每一个步骤:煮沸水 冲泡咖啡 把咖啡倒进杯子 加糖和加奶
private void boilWater() {
System.out.println("Boiling water");
}
private void steepTeaBag() {
System.out.println("Steeping the tea");
}
private void pourInCup() {
System.out.println("Pouring into cup");
}
private void addLemon() {
System.out.println("Adding Lemon");
}
}
从上面看到这里有许多的重复的代码,所有我们先做第一步的优化。提出相同的代码
分析:用热水泡咖啡或者茶和将饮料加入适当饮料这两个方法并没有被提出来。虽然他们有稍许不同,但是大部分是相同的。我们可以把浸泡(steep)和冲泡(brew)提供新的方法比如brew。加调料的也提出来叫addCondiments()
public abstract class CoffeineBeverage {
void prepareRecipe() {// 将每一个步骤给分离
boilWater();
brew();
pourInCup();
addCondiments();
}
// 实现算法的每一个步骤:煮沸水 冲泡咖啡 把咖啡倒进杯子 加糖和加奶
private void boilWater() {
System.out.println("Boiling water");
}
protected abstract void brew();
private void pourInCup() {
System.out.println("Pouring into cup");
}
protected abstract void addCondiments();
}
模板方法模式
1-定义模板方法模式:类图
2-优点:模板方法的结构不变,具体抽象方法交给子类去实现
3-其实通过上面的分析,这里就不重复提供代码了。模板方法还有一个很重要的概念-钩子:对方法进行挂钩
4-钩子方法:一个钩子方法由抽象类声明并实现,而子类会加以扩展。通常抽象类给出的实现是一个空实现,作为方法的默认实现。
- 上面的例子我们可以在抽象类CoffeineBeverage 里加一个customerWantsCondiments()的钩子方法。实际就是顾客是否想要配料
应用
- 用模板方法排序
这里提供部分代码具体请看jdk数据排序
public static void sort(Object[] a) {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a);
else
ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}
/** To be removed in a future release. */
private static void legacyMergeSort(Object[] a) {
Object[] aux = a.clone();
mergeSort(aux, a, 0, a.length, 0);
}
// 实际调用了mergeSort这个方法,现在看一下这个方法
@SuppressWarnings({"unchecked", "rawtypes"})
private static void mergeSort(Object[] src,
Object[] dest,
int low,
int high,
int off) {
int length = high - low;
// Insertion sort on smallest arrays
if (length < INSERTIONSORT_THRESHOLD) {
for (int i=low; i<high; i++)
for (int j=i; j>low &&
((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)
swap(dest, j, j-1);
return;
}
从上面可以看得到compareTo()就是个抽象方法需要子类来实现的,而swap()是个具体方法
- servlet中的应用
HttpServlet提供了一个service()方法,这个方法调用了7个do方法中的一个或几个,对客户端完成相应的响应。do方法需要由具体的子类提供。具体的可以查下JDK源码
模板方法模式和策略模式的比较
策略模式:定义一个算法族,可以让这些算法互换
模板方法:
- 定义一个算法大纲,虽然个别步骤可以有不同的实现细节,但算法的结构依然不变
- 方法的实现必须依赖于超类
- 对算法有更多的控制权,而且不会重复代码。因为策略采取的是委托模型所有他的耦合度低于模板方法