设计模式目录:
概述
模板模式是一种行为设计模式,它的实现思路是,创建一个桩方法,并且定义一些步骤让子类来实现。模板方法定义了一个算法的执行步骤,或者说能够提供一种默认的实现,这种实现概括一部分子类或者全部子类的共同部分。
举一个例子帮助理解,假设提供一种造房子的算法。算法的步骤就是模拟造房子的过程:建地基、建支撑,最后添加墙和窗户 – 1. Fundation,2. Pillars,3. Walls,4. Windows。最重要的一点就是不能改变此建造过程,比如不可能在没用地基的时候就开始建造窗户。这个例子中,我们就创建了一个模板方法 – 将使用不同的方法完成对房子的建造。
为了确保子类不能重写(override)这个模板方法,应当使用final。
模板方法抽象类
因为设计为一些方法的具体实现留待子类中,所以不得不安排基类为一个抽象类
HouseTemple.java
1 package cn.telling.test.action; 2 3 /** 4 * 5 * @ClassName: HouseTemplate TODO 6 * @author xingle 7 * @date 2015-9-24 下午2:04:22 8 */ 9 public abstract class HouseTemplate { 10 // template method, final so subclasses can't override 11 public final void buildHouse() { 12 buildFoundation(); 13 buildPillars(); 14 buildWalls(); 15 buildWindows(); 16 System.out.println("House is built."); 17 } 18 19 // default implementation 20 private void buildWindows() { 21 System.out.println("Building Glass Windows"); 22 } 23 24 // methods to be implemented by subclasses 25 public abstract void buildWalls(); 26 27 public abstract void buildPillars(); 28 29 private void buildFoundation() { 30 System.out.println("Building foundation with cement,iron rods and sand"); 31 } 32 33 }
buildHouse()是模板方法并定义了在建造房子过程中一系列方法的执行顺序。
WoodenHouse.java
1 package cn.telling.test.action; 2 3 /** 4 * 5 * @ClassName: WoodenHouse 6 * TODO 7 * @author xingle 8 * @date 2015-9-24 下午2:07:06 9 */ 10 public class WoodenHouse extends HouseTemplate{ 11 12 /** 13 * 14 * @Description: TODO 15 * @author xingle 16 * @data 2015-9-24 下午2:07:15 17 */ 18 @Override 19 public void buildWalls() { 20 System.out.println("Building Wooden Walls"); 21 22 } 23 24 /** 25 * 26 * @Description: TODO 27 * @author xingle 28 * @data 2015-9-24 下午2:07:15 29 */ 30 @Override 31 public void buildPillars() { 32 System.out.println("Building Pillars with Wood coating"); 33 } 34 35 }
此处也应当对其他方法进行重写,但是为了简便,此处没用完成。
GlassHouse.java
1 package cn.telling.test.action; 2 3 /** 4 * 5 * @ClassName: GlassHouse 6 * TODO 7 * @author xingle 8 * @date 2015-9-24 下午2:05:28 9 */ 10 public class GlassHouse extends HouseTemplate{ 11 12 /** 13 * 14 * @Description: TODO 15 * @author xingle 16 * @data 2015-9-24 下午2:05:39 17 */ 18 @Override 19 public void buildWalls() { 20 System.out.println("Building Glass Walls"); 21 } 22 23 /** 24 * 25 * @Description: TODO 26 * @author xingle 27 * @data 2015-9-24 下午2:05:39 28 */ 29 @Override 30 public void buildPillars() { 31 System.out.println("Building Pillars with glass coating"); 32 33 } 34 35 }
使用模板方法
用一个测试程序来测试此处已完成的模板方法。
HouseClient.java
1 package cn.telling.test.action; 2 3 /** 4 * 5 * @ClassName: HousingClient TODO 6 * @author xingle 7 * @date 2015-9-24 下午2:06:33 8 */ 9 public class HousingClient { 10 public static void main(String[] args) { 11 12 HouseTemplate houseType = new WoodenHouse(); 13 14 // using template method 15 houseType.buildHouse(); 16 System.out.println("************"); 17 18 houseType = new GlassHouse(); 19 20 houseType.buildHouse(); 21 } 22 23 }
注意,client正在调用基类的模板方法并且依赖于不同步骤的实现细节,即这些正在使用的方法,他们一些来着基类另一些来自子类。上述程序的输出:
模板方法的UML图
模板方法模式定义
模板方法模式定义一个操作中算法的骨架,而将这些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。
这个模式是用来创建一个算法的模板。什么是模板?如你所见的,模板就是一个方法。更具体地说,这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,由子类负责实现,这可以确保算法的结构保持不变,同时由子类提供部分实现。
JDK中模板方法模式的使用
- java.io.InputStream, java.io.OutputStream, java.io.Reader 以及 java.io.Writer 中所有非抽象方法。
- java.util.AbstractList, java.util.AbstractSet 以及 java.util.AbstractMap中所有非抽象方法。
重要提示
- 模板方法应该是由确定的步骤组成。这些步骤的顺序是固定的。基类与子类之间某些方法或者实现可以有所不同。模板方法应该是final的。
- 大多时候,子类的调用的方法是来自于超类。但是在模板模式中,超类的模板方法调用的方法却来至于子类,这就是著名的Hollywood原则-“don’t call us, we’ll call you”。
- 基类方法的默认实现被退化为钩子Hooks的概念,他们被设计在子类中被重写,如果你期望一些方法在子类中不被重写,你可以让他们为final。比如在例子中buildFoundation()方法是final的,因为不希望它在子类中被重写。
Java API中的模板方法
用模板方法排序
java数组类的设计者提供给我们一个方便的模板方法用来排序。
public static void sort(Object[] a) { if (LegacyMergeSort.userRequested) legacyMergeSort(a); else ComparableTimSort.sort(a); } /** 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() 方法包含排序算法,此算法依赖于compareTo() 方法的实现来完成算法。我们需要实现compareTo()方法,“填补”模板方法的缺憾。
/** * Src is the source array that starts at index 0 * Dest is the (possibly larger) array destination with a possible offset * low is the index in dest to start sorting * high is the end index in dest to end sorting * off is the offset to generate corresponding low, high in src * To be removed in a future release. */ 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() 方法。
但是数组无法继承,该如何使用sort()?
sort() 的设计者希望这个方法能使用于所有的数组,所以他们把sort() 变成事静态的方法,这样一来,任何数组都可以使用这个方法。它使用起来和它被定义在超类中是一样的。实际上,sort()并不是真正定义在超类中,所以sort() 方法需要知道你已经实现了compareTo()方法,否则就无法进行排序。
要达到这一点,设计者利用了Comparable接口。你必须实现这个接口,提供这个接口所声明的方法,也就是compareTo()。
鸭子的实现:
1 package cn.telling.test.action; 2 3 /** 4 * 5 * @ClassName: Duck 6 * TODO 7 * @author xingle 8 * @date 2015-9-24 下午3:32:47 9 */ 10 public class Duck implements Comparable<Object>{ 11 //我们需要让鸭子类实现Comparable 接口,因为我们无法真的让鸭子数组去继承数组 12 private String name ; 13 private int weight; 14 15 public Duck(String name,int weight){ 16 this.name = name; 17 this.weight = weight; 18 } 19 20 public String toString(){ 21 return name+" weight"+weight; 22 } 23 24 /** 25 * 排序所要的方法 26 * @Description: TODO 27 * @param o 28 * @return 29 * @author xingle 30 * @data 2015-9-24 下午3:42:43 31 */ 32 @Override 33 public int compareTo(Object o) { 34 //compareTo() 需要传入一只鸭子,和本身这种鸭子做比较 35 Duck otherDuck = (Duck) o; 36 if(this.weight<otherDuck.weight){ 37 return -1; 38 } else if(this.weight == otherDuck.weight){ 39 return 0; 40 } else { 41 return 1; 42 } 43 } 44 }
测试排序鸭子的程序:
1 package cn.telling.test.action; 2 3 import java.util.Arrays; 4 5 /** 6 * 7 * @ClassName: DuckSortTestDrive 8 * TODO 9 * @author xingle 10 * @date 2015-9-24 下午3:35:44 11 */ 12 public class DuckSortTestDrive { 13 14 public static void main(String[] args) { 15 Duck[] ducks = { new Duck("D1", 8), new Duck("D2", 6), 16 new Duck("D3", 2),new Duck("D4", 5) }; 17 System.out.println("Bofore sorting "); 18 dispaly(ducks); 19 //调用Array类的静态方法sort(),让后将鸭子数组当做参数传入 20 Arrays.sort(ducks); 21 System.out.println("After sorting "); 22 dispaly(ducks); 23 } 24 25 /** 26 * TODO 27 * @param ducks 28 * @author xingle 29 * @data 2015-9-24 下午3:39:17 30 */ 31 public static void dispaly(Duck[] ducks) { 32 for(int i = 0;i<ducks.length;i++){ 33 System.out.println(ducks[i]); 34 } 35 36 } 37 38 }
执行结果:
- Java中的模板模式
- [Head First设计模式]云南米线馆中的设计模式——模版方法模式
- 《First Head 设计模式》 第8章 封装算法:模板方法模式