先上类图:
模式观念
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
该设计模式主要针对这样一种场景:当要做一件事儿的时候,这件事儿的步骤是固定好的,但是每一个步骤的具体实现方式是不一定的。这样,我们可以把所有要做的事儿抽象到一个抽象类中,并在该类中定义一个模板方法。
简单举例
去银行的营业厅办理业务需要以下步骤:1.取号、2.办业务、3.评价。三个步骤中取号和评价都是固定的流程,每个人要做的事儿都是一样的。但是办业务这个步骤根据每个人要办的事情不同所以需要有不同的实现。我们可以将整个办业务这件事儿封装成一个抽象类:
public abstract class AbstractBusinessHandeler {
/**
* 模板方法
*/
public final void execute(){
getRowNumber();
handle();
judge();
}
/**
* 取号
* @return
*/
private void getRowNumber(){
System.out.println("rowNumber-00" + RandomUtils.nextInt());
}
/**
* 办理业务
*/
public abstract void handle(); //抽象的办理业务方法,由子类实现
/**
* 评价
*/
private void judge(){
System.out.println("give a praised");
}
}
类中定义了四个方法,其中getRowNumber、judge这两个方法是私有的非抽象方法。他们实现了取号和评价的业务逻辑,因为这两部分内容是通用的。还有一个抽象的handle方法,这个方法需要子类去重写,根据办理业务的具体内容重写该方法。还有一个模板方法就是final类型的execute方法,他定义好了需要做的事儿和做这些事儿的顺序。
现在,有了这个抽象类和方法,如果有人想要办理业务,那么只需要继承该AbstractBusinessHandeler并且重写handle方法,然后再使用该实现类的对象调用execute方法,即可完成整个办理业务的流程。
public class SaveMoneyHandler extends AbstractBusinessHandeler {
@Override
public void handle() {
System.out.println("save 1000");
}
public static void main(String []args){
SaveMoneyHandler saveMoneyHandler = new SaveMoneyHandler();
saveMoneyHandler.execute();
}
}//output:编号:rowNumber-001 save 1000 give a praised
钩子(do not call me, i will call you的经典体现)
在抽象模板里提供一个钩子,子类可根据情况选择实现方式,或者不去实现,钩子使得子类有决定父类型为的能力。加入上面的银行业务例子中加入会员功能(会员不用排队),改写抽象模板如下:
public abstract class AbstractBusinessHandeler {
public final void execute(){ //定义为final,禁止子类覆盖该方法
if(!isVip()){//如果顾客是vip,则不用排队
getRowNumber();
}
handle();
judge();
}
//抽象的钩子方法,由子类实现
public boolean isVip(){
return false; //父类提供默认实现
}
private void getRowNumber(){
System.out.println("rowNumber-00" + RandomUtils.nextInt());
}
public abstract void handle();
private void judge(){
System.out.println("give a praised");
}
}
荒野中模板(一些一眼认不出来的隐晦模板方法)
jdk中的arrays提供的对object进行sort的方法,源码如下:
/**
* Sorts the specified array of objects into ascending order, according
* to the {@linkplain Comparable natural ordering} of its elements.
* All elements in the array must implement the {@link Comparable}
* interface. Furthermore, all elements in the array must be
* <i>mutually comparable</i> (that is, {@code e1.compareTo(e2)} must
* not throw a {@code ClassCastException} for any elements {@code e1}
* and {@code e2} in the array).
*
* <p>This sort is guaranteed to be <i>stable</i>: equal elements will
* not be reordered as a result of the sort.
*
* <p>Implementation note: This implementation is a stable, adaptive,
* iterative mergesort that requires far fewer than n lg(n) comparisons
* when the input array is partially sorted, while offering the
* performance of a traditional mergesort when the input array is
* randomly ordered. If the input array is nearly sorted, the
* implementation requires approximately n comparisons. Temporary
* storage requirements vary from a small constant for nearly sorted
* input arrays to n/2 object references for randomly ordered input
* arrays.
*
* <p>The implementation takes equal advantage of ascending and
* descending order in its input array, and can take advantage of
* ascending and descending order in different parts of the the same
* input array. It is well-suited to merging two or more sorted arrays:
* simply concatenate the arrays and sort the resulting array.
*
* <p>The implementation was adapted from Tim Peters's list sort for Python
* (<a href="http://svn.python.org/projects/python/trunk/Objects/listsort.txt">
* TimSort</a>). It uses techniques from Peter McIlroy's "Optimistic
* Sorting and Information Theoretic Complexity", in Proceedings of the
* Fourth Annual ACM-SIAM Symposium on Discrete Algorithms, pp 467-474,
* January 1993.
*
* @param a the array to be sorted
* @throws ClassCastException if the array contains elements that are not
* <i>mutually comparable</i> (for example, strings and integers)
* @throws IllegalArgumentException (optional) if the natural
* ordering of the array elements is found to violate the
* {@link Comparable} contract
*/
public static void sort(Object[] a) {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a);
else
ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}
具体实现中部分代码如下:
@SuppressWarnings({"fallthrough", "rawtypes", "unchecked"})
private static void binarySort(Object[] a, int lo, int hi, int start) {
assert lo <= start && start <= hi;
if (start == lo)
start++;
for ( ; start < hi; start++) {
Comparable pivot = (Comparable) a[start]; //!!!!!!把object转换为Comparable
// Set left (and right) to the index where a[start] (pivot) belongs
int left = lo;
int right = start;
assert left <= right;
/*
* Invariants:
* pivot >= all in [lo, left).
* pivot < all in [right, start).
*/
while (left < right) {
int mid = (left + right) >>> 1;
if (pivot.compareTo(a[mid]) < 0)
right = mid;
else
left = mid + 1;
}
assert left == right;
/*
* The invariants still hold: pivot >= all in [lo, left) and
* pivot < all in [left, start), so pivot belongs at left. Note
* that if there are elements equal to pivot, left points to the
* first slot after them -- that's why this sort is stable.
* Slide elements over to make room for pivot.
*/
int n = start - left; // The number of elements to move
// Switch is just an optimization for arraycopy in default case
switch (n) {
case 2: a[left + 2] = a[left + 1];
case 1: a[left + 1] = a[left];
break;
default: System.arraycopy(a, left, a, left + 1, n);
}
a[left] = pivot;
}
}
最关键的部分就是Comparable pivot = (Comparable) a[start];
待比较的对象必须实现Comparable接口,这里的实现方式并不是传统的继承实现。
- 看起来更像是策略模式?
上面的代码确实是使用了组合的方式实现,但是策略模式被组合进来的类是被委托去实现整个算法内容的,而数组的排序这里排序不走基本都已经实现完了,就差具体比较逻辑了,所以更像是模板方法模式,很多模板方法模式估计比较难分辨出来,蛋蛋的忧伤。。。。
和工厂方法模式的区别?
实现方式简直一模一样,只是出发点不同,工厂方法是为了按需创建具体对象,模板方法完全是为了封装变化的算法,而且通常会有大于一个的抽象方法。
和策略模式的区别?
1.策略模式只对某个算法的整体进行封装,模板方法会去控制算法的步骤流程。(这一点其实不算是什么区别。。。)
2.模板方法在代码复用性,预留钩子等方面非常出色(因为抽象父类的存在)
3.策略模式是使用组合实现的,模板方法是继承(策略模式相当灵活,运行时也可动态替换算法。)
----自己使用的最多的模式之一,谢谢模板方法模式~~~致敬