比如说去银行办理业务
1.进门取号
2.填写单据
3.等待叫号
4.窗口办理
这是银行给我们提供的模板,大家都得遵守,但是呢?在填写单据的过程中,又存在着一些不同,这就是模板给我们一些发挥空间。
模板方法会提供一些算法框架,但是一些特点步骤实现需要子类进行实现。留给特定子类来实现。
定义如下:定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
使用场景分析:
会发现有很多共性,比如第一步和第三步;
代码实现
首先:先理一下思路,模板方法设计:
/**
* 抽象基类,为所有子类提供一个算法框架
* 提神饮料
*/
public abstract class RefreshBeverage {
/**
* 不希望子类进行更改
* 封装了所有子类共同遵循的算法框架
*/
public final void prepareBeverageTemplate(){
//步骤1 将水煮沸
boilWater();
//步骤2 泡制饮料
brew();
//步骤3 将饮料倒入杯中
pourInCup();
//步骤4 加入调味料
addCondiments();
}
/**
* 将水煮沸 是所有子类都一样的 就声明为private
*/
private void boilWater(){
System.out.println("将水煮沸");
}
/**
* 将饮料倒入杯中
* 因为不变 所以直接用private
*/
private void pourInCup(){
System.out.println("将饮料倒入杯中");
}
/**
* 抽象的基本方法
* 泡制饮料
*/
protected abstract void brew();
/**
* 加入调料
*/
protected abstract void addCondiments();
}
这里prepareBeverageTemplate() 代表的是提神饮料的模板方法,故后面跟上Template,到时候子类调用它即可。
这里boilWater() pourInCup(),这两个方法是模板方法中固定的,不希望子类进行访问,同时子类也没必要进行访问,故用private来进行修饰。现在模板类和模板方法都已经写好了。下面进行实现类的书写。
public class Coffee extends RefreshBeverage {
@Override
protected void brew() {
System.out.println("向沸水冲泡咖啡");
}
@Override
protected void addCondiments() {
System.out.println("加入咖啡和牛奶");
}
}
这是咖啡实现类
public class Tea extends RefreshBeverage {
@Override
protected void brew() {
System.out.println("用80度的热水浸泡茶叶五分钟");
}
@Override
protected void addCondiments() {
System.out.println("加入柠檬");
}
}
这是茶实现类。
然后就可以进行测试了:
public class RefreshBeverageTest {
public static void main(String[] args) {
Coffee coffee = new Coffee();
coffee.prepareBeverageTemplate();
System.out.println("咖啡好了");
System.out.println("=======================");
Tea tea = new Tea();
tea.prepareBeverageTemplate();
System.out.println("茶泡好了");
}
}
结果如下:
可以明显的感觉到,子类不同,其产生的结果也不同。
而且,加入模板方法,就可以少些很多代码。
但是,上面写的模板方法还是有一些问题,比如说我想喝茶,但是我不想加入柠檬,不想加入任何东西。由于模板方法中加入了这一环节,所以,就显得很臃肿。不是我的本意。这时候就需要解决:
解决方法如下:让用户自己判断需不需要进行加入:
//步骤4 加入调味料
if (isCustomerWantsCondiments()) {
addCondiments();
}
protected boolean isCustomerWantsCondiments() {
return true;
}
在父类模板方法中加入了一个钩子函数,同时,该函数使用protected来进行修饰;当子类不希望加入调料时,就可以重写该方法,使其返回为false。
public class Tea extends RefreshBeverage {
@Override
protected void brew() {
System.out.println("用80度的热水浸泡茶叶五分钟");
}
@Override
protected void addCondiments() {
System.out.println("加入柠檬");
}
@Override
protected boolean isCustomerWantsCondiments() {
return false;
}
}
这样在执行模板方法,就不会
if (isCustomerWantsCondiments()) {
addCondiments();
}
执行这行语句了。
结果如下:
在抽象基类中 有1.基本方法(肯定有的步骤,可以直接实现的)2.抽象方法(根据子类不同而不同的方法,交给子类实现)3.可选的钩子 4。将基本方法抽象方法钩子汇总而成一个模板方法,一定要用用final进行修饰。不能改变整体逻辑。
在子类中:1.实现基类中的抽象方法 2.可选的覆盖一些钩子方法
模板方法模式的适用场景
1.算法或者操作遵循相似的逻辑
2.重构,当面对之前残留下来的系统时,如果有新需求,发现和之前的功能有一些相似性时,就可以抽取成一个父类,写成一个模板方法。
模板方法的优点