前言
该文章摘自https://baijiahao.baidu.com/s?id=1639309117007251878&wfr=spider&for=pc
模板方法模式也是也是比较容易理解的,就比如说做饭,同样的步骤不同的人做味道是不一样的。或者是造汽车,同样的步骤,造车厂商不一样,造出来的汽车质量不同。这就是模板方法模式。这篇文章将通过案例详细的讲解一下模板方法模式。
1、概念
它定义一个操作中的算法的框架,而将一些步骤延迟到了子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些步骤。
2、例子
我们拿做饭的例子来说明。比如就做个西红柿鸡蛋吧。我们可以简单地定义一下步骤:
第一步:放油
第二步:放西红柿
第三步:放鸡蛋
当然真正做起来可能要比这复杂的多,就不必纠结于此了。上面我们提到过,同样的步骤其实不同的人做出来的饭是不一样的。我们就拿自己和五星级大厨来比较吧。人总是要有点自信心,虽然会被打脸。
不过在代码实现之前,我们可以先看一下模板方法模式的类图:
我们就拿自己和大厨比较,我们可以看到在这里其实有两个角色:
(1)模板方法:它定义了一系列方法,提供了一个骨架。
(2)具体类:实现上面模板方法类提供的骨架。不同的具体类实现这个模板方法的骨架方式是不一样的。
下面代码实现一下。
3、代码实现
第一步:定义模板方法类(cook的骨架)
第二步:定义具体类(我和大厨)
首先是我做饭
然后是大厨做饭
第三步:模拟炒菜的过程
结果
看结果我们就能知道,炒西红柿鸡蛋的过程是一样的,但是实现起来却不一样,就像买衣服,模特身上穿起来很好看,但是自己买的时候穿起来那真是惨不忍睹,这就是模板方法模式,我们对其进行一个总结。
4、优缺点
先说一下他的优点吧:
(1)把不可改变的封装起来,把能够改变的扩展开来
(2)它把很多类的共同操作给封装了起来,利于维护
(3)其实我们发现,我们在定义行为时候都是由父类去定义,然后子类去实现即可。
再聊一下他的缺点:
缺点很简单,我们发现虽然它把一些类的共同操作封装了起来,但是当这些类比较多时,效果就不好了,因为有一个拓展子类都需要继承它,子类多了就不好了。
5、钩子函数
钩子就是给子类一个授权,让子类来决定模板方法的逻辑执行。就比如在炒西红柿鸡蛋的时候,由子类去决定是否要加调料。我们去实现一下:
第一步:定义模板类:(增加了钩子函数 isAddOil() )
public abstract class Cook {
//放油
public abstract void oil();
//放鸡蛋
public abstract void egg();
//放西红柿
public abstract void tomato();
//钩子方法:让子类决定是否加油
public boolean isAddOil(){
return true;
}
//把做饭的方法封装起来(即框架方法)
final public void cook(){
egg();
tomato();
//由子类决定是否添加
if(isAddOil()){
oil();
}
}
}
第二步:定义具体类
首先是自己:
package template模板方法;
//自己做饭
public class MyCook extends Cook {
//默认添加油
private boolean addOilFlag = true;
//用户设置是否加油
@Override
public boolean isAddOil() {
return addOilFlag;
}
public void setAddOilFlag(boolean isAddOil) {
addOilFlag = isAddOil;
}
@Override
public void oil() {
System.out.println("自己: 放了十斤油");
}
@Override
public void egg() {
System.out.println("自己:放了一个鸡蛋");
}
@Override
public void tomato() {
System.out.println("自己:放了十个西红柿");
}
}
然后是大厨
package template模板方法;
//大厨做放
public class ChefCook extends Cook {
//默认添加油
private boolean addOilFlag = true;
@Override
public boolean isAddOil() {
return addOilFlag;
}
public void setAddOilFlag(boolean addOilFlag) {
this.addOilFlag = addOilFlag;
}
@Override
public void oil() {
System.out.println("大厨: 放了适量的油");
}
@Override
public void egg() {
System.out.println("大厨: 放了适量的鸡蛋");
}
@Override
public void tomato() {
System.out.println("大厨: 放了适量的西红柿");
}
}
第三步:测试一下:
//模拟炒菜过程
public class TestCook {
public static void main(String[] args) {
//我不放油
MyCook myCook = new MyCook();
myCook.setAddOilFlag(false);
myCook.cook();
//大厨做饭放油是默认的,不用指定
ChefCook chefCook = new ChefCook();
chefCook.cook();
}
}
运行结果
钩子函数就是这样使用的。子类就可以设置其逻辑是否执行。自己动手写一遍是理解最好的方式。