目錄
1.概念
在父類中定義算法的框架,讓子類根據業務需要,來填充框架的具體實現步驟。模板方法使子類可以重新定義算法的某些實現,而無需更改算法的結構。
爲確保子類不會重寫template方法,應聲明模板方法爲final
2.適用性
- 父類實現算法的不變部分,並讓子類來實現可能變化的行爲。
- 子類之間的共同行爲應分解並集中在一個共同類中,以避免代碼重複。
3.程式範例
我們以農民伯伯播種爲例,比如去年種植了小麥,今年想種植玉米了,而種植的流程大致分爲:購買種子、播撒、澆水等,不管是種啥,都按這個流程走,那麼這個現象,就可以歸結爲一個模板方法模式了。
我們看看具體的代碼實現吧!
3.1.抽象類來封裝算法的框架和核心算法
public abstract class AbstractPlantMethod {
/**
* 購買種子
*/
public abstract String buySeeds();
/**
* 播撒
*/
public abstract void sow(String seeds);
/**
* 澆水
*/
public abstract void watering(String seeds);
/**
* 種植方法(定義爲final,防止子類重寫該核心算法)
*/
public final void planting() {
String seeds = this.buySeeds();
this.sow(seeds);
this.watering(seeds);
}
}
3.2.播種小麥
public class WheatMethod extends AbstractPlantMethod {
@Override
public String buySeeds() {
System.out.println("購買小麥作物中...");
return "wheats";
}
@Override
public void sow(String seeds) {
System.out.println("正在播撒" + seeds + "中...");
}
@Override
public void watering(String seeds) {
System.out.println("正在給" + seeds + "澆水中...");
}
}
3.3.播種玉米
public class CornMethod extends AbstractPlantMethod {
@Override
public String buySeeds() {
System.out.println("購買玉米作物中...");
return "corns";
}
@Override
public void sow(String seeds) {
System.out.println("正在播撒" + seeds + "中...");
}
@Override
public void watering(String seeds) {
System.out.println("正在給" + seeds + "澆水中...");
}
}
3.4.農民伯伯
public class Farmers {
private AbstractPlantMethod plantMethod;
public Farmers(AbstractPlantMethod plantMethod) {
this.plantMethod = plantMethod;
}
public void plant() {
plantMethod.planting();
}
public void changeMethod(AbstractPlantMethod plantMethod) {
this.plantMethod = plantMethod;
}
}
3.5.客戶端調用者
public class Client {
public static void main(String[] args) {
Farmers farmers = new Farmers(new WheatMethod());
//播種小麥
farmers.plant();
farmers.changeMethod(new CornMethod());
//播種玉米
farmers.plant();
}
}
3.6.結果打印
購買小麥作物中...
正在播撒wheats中...
正在給wheats澆水中...
購買玉米作物中...
正在播撒corns中...
正在給corns澆水中...
4.模板方法模式在JDK1.8中的使用
ArrayList相信我們每天都在用,但是我們從來沒關注過它的父類和實現的接口,下面我列出它的父類AbstractList的部分關鍵代碼。
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
//get方法(抽象的方法,必須讓子類按照自己的業務去實現)
abstract public E get(int index);
//addAll方法,允許子類去重寫該方法,如果不重寫,也可用父類中已經定義好的方法。
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
boolean modified = false;
for (E e : c) {
add(index++, e);
modified = true;
}
return modified;
}
}
再來看看ArrayList中的關鍵代碼吧
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//實現的get方法
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
//重寫父類的addAll方法
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
}
5.模板方法模式在Spring中的使用
spring在構建Servlet體系的時候,用到了我們的模板方法模式,我們先來看父類的關鍵代碼。
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
//重寫了Javax提供的init方法,並將方法用final修飾,那麼它的子類就沒有權限修改init方法了
@Override
public final void init() throws ServletException {
// Set bean properties from init parameters.
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// Let subclasses do whatever initialization they like.
initServletBean();
}
}
而它的子類FrameworkServlet和DispatcherServlet就不能再去重寫init方法了,只需根據需要重寫部分方法,公用的是一個init方法。
6.總結
如果我們希望子類不要修改父類的方法,只需要加上final修飾即可;如果希望子類一定重寫父類的方法,就將父類的方法用abstract修飾;如果子類可以修改也可以不修改,就可以像addAll方法那樣設計即可。