例如有两个处理逻辑:泡茶、冲咖啡;
他们的基本流程(算法)是相同的:煮开水、冲泡、倒进杯子、加入调料。只不过具体到个别步骤可能有差异。
如果分成两个类来实现,就会存在重复代码。
——可以将公共的部分(算法)提到父类中;由各个子类实现每个具体步骤。
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses.
Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
模板方法模式在一个方法中定义算法的框架,而将一些算法步骤延迟到子类中定义。使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。——即,封装算法
public abstract class AbstractClass { //template method ----final final void templateMethod() { primitiveOperation1(); primitiveOperation2(); concreteOperation(); hook(); if( hook2() ){ //钩子可影响算法行为 } } protected abstract void primitiveOperation1(); protected abstract void primitiveOperation2(); final void concreteOperation() { ...... } //父类中可以有“默认不做事的方法”;子类可以视情况决定要不要覆盖它 void hook() { }//empty method boolean hook2() { return true; } }
子类执行的结果会影响父类的结果,子类影响父类
// Arrays public static void sort(Object[] a) { ... ComparableTimSort.sort(a); } // ComparableTimSort static void sort(Object[] a, int lo, int hi) { ... binarySort(a, lo, hi, lo + initRunLen); } //算法框架 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++) { @SuppressWarnings("unchecked") Comparable<Object> pivot = (Comparable) a[start]; // Set left (and right) to the index where a[start] (pivot) belongs .... while (left < right) { int mid = (left + right) >>> 1; if (pivot.compareTo(a[mid]) < 0) //compareTo这个算法步骤,是由各个Comparable的子类定义的 right = mid; else left = mid + 1; } .... } }
Q:这个算法框架并不是设计在父类中,而是在一个工具类中
A:是的,与教科书上模板方法的定义有差异;因为sort要适用于所有数组,所以提供了一个Arrays工具类。但仍然是模板方法模式
//算法框架 public int read(byte b[], int off, int len) throws IOException { ... int c = read(); ... } //算法步骤由子类实现 public abstract int read() throws IOException;
// JFrame public void update(Graphics g) { paint(g); }
public class MyFrame extends JFrame { public MyFrame(){ super(); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(300,300); this.setVisible(true); } @Override public void paint(Graphics g){ //重定义算法步骤 super.paint(g); g.drawString("I rule !", 100, 100); } public static void main(String[] args){ MyFrame frame = new MyFrame(); } }
// Applet public void init() { //什么也不做的hook }
// Beans public static Object instantiate(ClassLoader cls, String beanName, BeanContext beanContext, AppletInitializer initializer) throws IOException, ClassNotFoundException { // If it was deserialized then it was already init-ed. // Otherwise we need to initialize it. if (!serialized) { // We need to set a reasonable initial size, as many // applets are unhappy if they are started without // having been explicitly sized. applet.setSize(100,100); applet.init(); //调用hook } } return result; }
Applet中的init()/start()/stop()/destroy()/paint()这些方法,都是hook。