该项目源码地址:https://github.com/ggb2312/JavaNotes/tree/master/design-pattern (设计模式相关代码与笔记)
1. 定义
定义了一个算法的骨架,并允许子类为一个或多个步骤提供实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤
2. 适用场景
- 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现
- 各子类中公共的行为被提取出来并集中到一个公共父类中,从而避免代码重复
3. 相关设计模式
模板方法模式和工厂方法模式
- 工厂方法模式是模板方法模式的一种特殊实现
模板方法模式和策略模式
- 策略模式是使不同的算法可以相互替换,并且不影响客户端的使用。模板方法模式定义算法的执行流程,不同的算法交给子类去实现
4. 模式实例
4.1 常规-模板方法
背景:学炒菜,手撕包菜 & 蒜蓉炒菜心**
(1)创建抽象模板结构(Abstract Class)
炒菜的步骤
public abstract class Abstract {
//模板方法,用来控制炒菜的流程 (炒菜的流程是一样的-复用)
//申明为final,不希望子类覆盖这个方法,防止更改流程的执行顺序
final void cookProcess() {
//第一步:倒油
this.pourOil();
//第二步:热油
this.HeatOil();
//第三步:倒蔬菜
this.pourVegetable();
//第四步:倒调味料
this.pourSauce();
//第五步:翻炒
this.fry();
}
//定义结构里哪些方法是所有过程都是一样的可复用的,哪些是需要子类进行实现的
//第一步:倒油是一样的,所以直接实现
void pourOil() {
System.out.println("倒油");
}
//第二步:热油是一样的,所以直接实现
void HeatOil() {
System.out.println("热油");
}
//第三步:倒蔬菜是不一样的(一个下包菜,一个是下菜心)
//所以声明为抽象方法,具体由子类实现
abstract void pourVegetable();
//第四步:倒调味料是不一样的(一个下辣椒,一个是下蒜蓉)
//所以声明为抽象方法,具体由子类实现
abstract void pourSauce();
//第五步:翻炒是一样的,所以直接实现
void fry() {
System.out.println("炒啊炒啊炒到熟啊");
}
}
(2) 创建具体模板(Concrete Class)
炒”手撕包菜“和”蒜蓉炒菜心“的具体步骤
//炒手撕包菜的类
public class ConcreteClass_BaoCai extends Abstract {
@Override
public void pourVegetable() {
System.out.println("下锅的蔬菜是包菜");
}
@Override
public void pourSauce() {
System.out.println("下锅的酱料是辣椒");
}
}
//炒蒜蓉菜心的类
public class ConcreteClass_CaiXin extends Abstract {
@Override
public void pourVegetable() {
System.out.println("下锅的蔬菜是菜心");
}
@Override
public void pourSauce() {
System.out.println("下锅的酱料是蒜蓉");
}
}
(3)测试
public class Test {
public static void main(String[] args) {
//炒 - 手撕包菜
ConcreteClass_BaoCai BaoCai = new ConcreteClass_BaoCai();
BaoCai.cookProcess();
//炒 - 蒜蓉菜心
ConcreteClass_CaiXin CaiXin = new ConcreteClass_CaiXin();
CaiXin.cookProcess();
}
}
4.2 扩展-钩子方法
如果想炒两盘青菜,一盘是不放调味料,一盘放调味料怎么办呢。
按照模板方法的正常步骤就是,写一个炒青菜子类ConcreteClass_Qingcai_NotPourSauce空实现倒调味料方法,写一个炒青菜子类ConcreteClass_Qingcai实现倒调味料方法。然后炒不放调味料的青菜使用ConcreteClass_Qingcai_NotPourSauce,炒放调味料的青菜
使用ConcreteClass_Qingcai。
如果想要炒更多青菜怎么办呢?
可以在抽象的模板里面添加钩子方法,在抽象的执行流程中,根据钩子函数决定是否调用某个步骤。
public abstract class Abstract {
//模板方法,用来控制炒菜的流程 (炒菜的流程是一样的-复用)
//申明为final,不希望子类覆盖这个方法,防止更改流程的执行顺序
final void cookProcess() {
//第一步:倒油
this.pourOil();
//d第二步:热油
this.HeatOil();
//第三步:倒蔬菜
this.pourVegetable();
//第四步:倒调味料
// 这里是否需要倒调味料交由钩子方法来决定
if (needPourSauce()) {
this.pourSauce();
}
//第五步:翻炒
this.fry();
}
//定义结构里哪些方法是所有过程都是一样的可复用的,哪些是需要子类进行实现的
//第一步:倒油是一样的,所以直接实现
void pourOil() {
System.out.println("倒油");
}
//第二步:热油是一样的,所以直接实现
void HeatOil() {
System.out.println("热油");
}
//第三步:倒蔬菜是不一样的(一个下包菜,一个是下菜心)
//所以声明为抽象方法,具体由子类实现
abstract void pourVegetable();
//第四步:倒调味料是不一样的(一个下辣椒,一个是下蒜蓉)
//所以声明为抽象方法,具体由子类实现
abstract void pourSauce();
//第五步:翻炒是一样的,所以直接实现
void fry() {
System.out.println("炒啊炒啊炒到熟啊");
}
/**
* 我们就要给这个倒调味料声明一个钩子方法
*/
protected boolean needPourSauce() {
return true;
}
}
这样我们只需要一个炒青菜子类。子类可以选择性的重写钩子方法,决定是否倒入调料。
5. 优缺点
优点:
- 提高复用性
- 提高扩展性
- 符合开闭原则
缺点:
- 类数目增加
- 增加了系统实现的复杂度
- 如果父类添加新的抽象方法,所有子类都要改一遍
6. 扩展-JDK1.7源码以及框架中的模板方法
6.1 jdk
java.util.AbstractList定义一套算法模板,如get(),set()等等,由子类实现。
6.2 servlet
javax.servlet.http.HttpServlet定义一套算法模板,如doGet()、doPost()等等,由子类实现。。
6.3 mybatis
org.apache.ibatis.executor.BaseExecutor