个人备忘:
摘自慕课网视频:http://www.imooc.com/learn/145
一、模板方法
- 定义:
定义了一个操作算法框架,而将一些步骤延迟到子类中实现,使得子类在不改变一个算法结构的同时就可以重新定义该算法的某些特定步骤。
- 步骤:
1.创建抽象基类,为所有子类提供一个算法框架,并且在类中提供一个final的模板方法,使得抽象基类定义了算法框架而禁止子类对算法进行任何修改
2.在模板类中实现基本方法,子类实现的方法设置成abstract(抽象方法)在别的类中实现
- 基本实现思想 :
1、 算法框架中分离出变与不变的部分
2、将变化的算法,延迟实现(交由具体的子类实现)
二、钩子函数
- 定义:
用钩子函数实现子类对算法框架个性化的扩展
- 基本思想:
框架通过提供一个个的钩子,使框架具备了更大的灵活性。不想执行算法框架中的某些个步骤,我们可以脱钩,如果想执行的话,我们可以挂钩。
- 实现 :
在抽象类中,提供protected钩子方法。这是个实现的或空的方法。这样子类就可以选择覆写-持钩,也可以选择不覆写-脱勾。
- 使用:
提供一个isXXX类型的钩子方法。用该方法控制算法框架中 某个步骤是否执行 ,子类不覆写这个方法,就是脱钩,仍按框架逻辑执行,一旦覆写,就是挂钩,将改变框架算法方向,按子类逻辑执行。
三、模板方法使用
使用模版方法模式一般有两种类型的类, 分别是抽象基类与具体子类.
- 抽象基类
1. 提供基本方法
这种方法对于不同子类而言是相同的,具有共性的,因此可以在抽象基类中定义的方法,例如biliWater()、pourInCup()。
2. 抽象方法
只知道原则,而不知道实现细节,需要将它延迟到子类中实现的步骤,例如brew()、addCondiments()。
3. 可选钩子
Hook,钩子函数,提供一个默认或者空的实现,具体的子类可以自行决定是否挂钩或者如何挂钩,从而影响算法实现
4. 讲上述三种方法根据业务逻辑汇总成Template方法
声明为final,使其不能被子类所覆写。模板方法模式要遵循的原则:子类可以替换掉父类中的可变逻辑,但不能改变整体逻辑结构。(好莱坞原则)
package com.imooc.pattern.template;
/*
* 抽象基类,为所有子类提供一个算法框架
*
* 提神饮料
*/
public abstract class RefreshBeverage {
/*
* 制备饮料的模板方法
* 封装了所有子类共同遵循的算法框架
*/
public final void prepareBeverageTemplate(){
//步骤1 将水煮沸
boilWater();
//步骤2 泡制饮料
brew();
//步骤3 将饮料倒入杯中
pourInCup();
if(isCustomerWantsCondiments()){
//步骤4 加入调味料
addCondiments();
}
}
/*
* Hook, 钩子函数,提供一个默认或空的实现
* 具体的子类可以自行决定是否挂钩以及如何挂钩
* 询问用户是否加入调料
*/
protected boolean isCustomerWantsCondiments() {
return true;
}
/*
* 基本方法,将水煮沸
*/
private void boilWater() {
System.out.println("将水煮沸");
}
/*
* 基本方法,将饮料倒入杯中
*/
private void pourInCup() {
System.out.println("将饮料倒入杯中");
}
/*
* 抽象的基本方法,泡制饮料
*/
protected abstract void brew();
/*
* 抽象的基本方法, 加入调味料
*/
protected abstract void addCondiments();
}
- 具体子类
1. 实现基类中的抽象方法
提供自己个性化具体的独特的方法的实现,例如brew()、addCondiments()
2. 覆盖钩子方法(可选)
影响算法的局部行为,例如茶不加调料,咖啡加入了调料。
package com.imooc.pattern.template;
/*
* 具体子类,提供了咖啡制备的具体实现
*/
public class Coffee extends RefreshBeverage {
@Override
protected void brew() {
System.out.println("用沸水冲泡咖啡");
}
@Override
protected void addCondiments() {
System.out.println("加入糖和牛奶");
}
}
package com.imooc.pattern.template;
/*
* 具体子类,提供了制备茶的具体实现
*/
public class Tea extends RefreshBeverage {
@Override
protected void brew() {
System.out.println("用80度的热水浸泡茶叶5分钟");
}
@Override
protected void addCondiments() {
System.out.println("加入柠檬");
}
@Override
/*
* 子类通过覆盖的形式选择挂载钩子函数
* @see com.imooc.pattern.template.RefreshBeverage#isCustomerWantsCondiments()
*/
protected boolean isCustomerWantsCondiments(){
return false;
}
}
package com.imooc.pattern.template;
public class RefreshBeverageTest {
public static void main(String[] args) {
System.out.println("制备咖啡...");
RefreshBeverage b1 = new Coffee();
b1.prepareBeverageTemplate();
System.out.println("咖啡好了!");
System.out.println("\n******************************************");
System.out.println("制备茶...");
RefreshBeverage b2 = new Tea();
b2.prepareBeverageTemplate();
System.out.println("茶好了!");
}
}
- 实现要素:
准备一个抽象类,将部分逻辑以具体方法的形式实现,然后声明一些抽象方法交由子类实现剩余逻辑,用钩子方法给予子类更大的灵活性。最后将方法汇总构成一个不可改变的模板方法。
- 适用场景:
1.算法或操作遵循相似的逻辑
2.重构时(把相同的代码抽取到父类)
3.重要、复杂的算法,核心算法设计为模板算法
- 优点:
(1)封装性好(2)复用性好 (3)屏蔽细节(4)便于维护。
- 缺点:
(1)继承限制(Java语言是单继承语言),单继承会使得更多情况不可用,新类引入困难。