模板方法模式(Template Method)及应用

模板方法模式定义了一个算法的步骤,并允许子类别为一个或多个步骤提供其实践方式。让子类别在不改变算法架构的情况下,重新定义算法中的某些步骤。(来自百度百科)

  模板方法模式在框架中经常使用,学习此模式后,对于阅读源码能力会有很大的提升。我准备先描述生活中的实际场景,引申出模板方式模式,然后分析此模式在JDK中的使用,最后分析在框架SpringMVC中的使用。

1、冲咖啡和泡奶茶如何抽取成一个模板

1.1 模板思路

冲咖啡步骤

(1)把水煮沸

(2)把沸水冲咖啡

(3)倒进杯子里

(4)加奶加糖

泡奶茶步骤

(1)把水煮沸

扫描二维码关注公众号,回复: 4324342 查看本文章

(2)把沸水泡茶叶

(3)倒进杯子里

(4)加点奶

  可以发现,冲咖啡和泡奶茶有两个步骤是一样的:把水煮沸和倒入杯子中,有两个步骤不一样。那么是不是将两个相同的方法抽取在父类当中,用final修饰,让咖啡和奶茶两个子类去继承这个父类,两个子类就具有了父类把水煮沸和倒入杯子中的能力。再去分别实现另外两个步骤就可以了,最后完成冲咖啡和泡奶茶的流程。

  可是,这种做法对于父类来说,整个步骤是不可控的,虽然子类具有了父类的方法,但是采用了怎样的步骤顺序去制作咖啡和奶茶,父类完全不清楚。所以,这里要做出一些改进,如果我们能在父类中将咖啡或者奶茶的每一个步骤都定义好,需要子类实现的方法定义成抽象类,然后将整个流程封装在一个方法里,子类继承父类后直接调用父类的这个方法,就能完全控制住整个操作步骤了。

1.2 具体的代码实现

/**
 * 茶和咖啡的抽象父类
 */
public abstract class CaffeineBeverage {
    public final  void prepareRecipe(){
        boilWater();
        brew();
        pourCup();
        addCondiment();
    }

    abstract void brew();

    abstract void addCondiment();

    void boilWater(){
        System.out.println("烧水");
    }

    final void pourCup(){
        System.out.println("倒入杯子中");
    }

}
public class Coffee extends CaffeineBeverage {
    @Override
    void brew() {
        System.out.println("加入咖啡");
    }

    @Override
    void addCondiment() {
        System.out.println("加入奶泡和糖");
    }
}
public class Tea extends CaffeineBeverage {
    @Override
    void brew() {
        System.out.println("放入茶叶");
    }

    @Override
    void addCondiment() {
        System.out.println("加点奶");
    }
}
public class TemplateMethodPattern {
    public static void main(String[] args) {
        Coffee coffee = new Coffee();
        coffee.prepareRecipe();
    }
}

1.3 代码分析

  使用了模板方法,子类只需要去继承父类并实现父类中的抽象方法,但具体的执行步骤还是在父类CaffeineBeverage中定义并用final修饰的,这一点保证了步骤的统一。另外,在父类中具体的方法并不一定要被final修饰,可以由子类去决定用不用或怎么使用。重点的是模板方法的思想,父类控制流程,子类控制某些步骤。

  另外,还有一点,创建模板对象时,要不要用模板对象去接收?这个问题我的想法是可以,看具体的使用场景。

2、 Java API中的模板方法思想

2.1 数组的排序

平时我们对操作数据排序时,会使用Arrays.sort(Object[] o)方法。sort()调用的最重要的方法是mergeSort(),他就是一个模板方法,依赖实现类实现comparaTo()完成排序。sort()方法和mergeSort()方法这些都是静态方法之间的调用,在比较数据中的对象时,会先将对象强转成Comparable,调用CompareTo()进行比较,如果需要交换位置,则调用swap()。这也就是为什么,当我们自定义类排序时,一定要实现Compareble并重写compareTo()。

public class Arrays {
...
    public static void sort(Object[] a) {
        if (LegacyMergeSort.userRequested)
            legacyMergeSort(a);
        else
            ComparableTimSort.sort(a, 0, a.length, null, 0, 0);
    }
    private static void legacyMergeSort(Object[] a) {
        Object[] aux = a.clone();
        mergeSort(aux, a, 0, a.length, 0);  
    }

  //排序中的模板方法,依赖comparaTo()方法的实现完成
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; } // Recursively sort halves of dest into src int destLow = low; int destHigh = high; low += off; high += off; int mid = (low + high) >>> 1; mergeSort(dest, src, low, mid, -off); mergeSort(dest, src, mid, high, -off); // If list is already sorted, just copy from src to dest. This is an // optimization that results in faster sorts for nearly ordered lists. if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) { System.arraycopy(src, low, dest, destLow, length); return; } // Merge sorted halves (now in src) into dest for(int i = destLow, p = low, q = mid; i < destHigh; i++) { if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0) dest[i] = src[p++]; else dest[i] = src[q++]; } } ... }

2.2 代码分析

  看到这里,可能会有些迷惑,这里为什么没有用继承,模板方法的核心不应该是继承吗?其实并不是这样,只要能让流程在类中控制住,子类或实现类实现需要实现的部分,这就是一个完整的模板方法模式。sort方法是静态方法,就可以在所有数组中使用,只需要这个数组中的对象实现Comparable,这样用起来更加方便,假如是继承的话,是没法保证每一个类都能继承Comparable,毕竟Java只支持单继承。

3、SpringMVC中的模板方法

未完待续...

  

猜你喜欢

转载自www.cnblogs.com/handler4J/p/10051909.html