自我修养之模板设计模式
定义:
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
解决什么问题?
主要解决同一接口的不同实现中包含了重复的代码,可抽取公共的代码片段.
如何解决?
将通用的逻辑抽取出来,定义一个抽象方法,由不同的子类去实现不同的逻辑
案例需求:
以热饮料为例:
盖伦:要喝热咖啡
提莫:要喝热奶茶
如果不使用模板设计模式
/**
* @ Author :kwj.
* @ Date :Created in 23:15 2020/3/30 0030
* @ Description:热茶
* @ Modified By:
* 欢迎关注公众号: ProgramLife 定期推送系列文章
*/
public class TeaDrink {
public static void main(String[] args) {
//第一步烧热水
System.out.println("烧热水");
//第二步将水导入杯子
System.out.println("倒入杯子");
//第三步制作咖啡
System.out.println("制作茶");
}
}
//热咖啡
public class CoffeeDrink {
public void markDrink(){
//第一步烧热水
System.out.println("烧热水");
//第二步将水导入杯子
System.out.println("倒入杯子");
//第三步制作咖啡
System.out.println("制作咖啡");
}
}
经过对比发现:上述两个类唯一不同的区别在于第三步,这刚好满足使用模板设计模式,抽取第一步和第二步为公共逻辑,将第三步进行抽象,交给子类去实现,代码如下:
创建公共接口
public interface HotDrink {
public void makeHotDrink();
/**
* 添加调料
*/
public void addCondiments();
}
编写抽象类,抽取公共逻辑
public abstract class AbstractHotDrink implements HotDrink{
@Override
public void makeHotDrink() {
boilWaterAndPour();
if (customCondiments()) {
addCondiments();
}
makeDrink();
}
/**
* 抽象方法 交给子类实现
*/
protected abstract void makeDrink();
/**
* 即抽取公共逻辑
* 烧热水+倒入杯子
*/
public void boilWaterAndPour(){
//第一步烧热水
System.out.println("烧热水");
//第二步将水导入杯子
System.out.println("倒入杯子");
}
/**
* 钩子函数 默认返回true
* 即默认都加调料
* @return
*/
public boolean customCondiments(){
return true;
}
/**
* 默认都加糖,可被子类覆盖(目的是为了防止盖伦想加糖,提莫不想加糖想加其他的)
*/
@Override
public void addCondiments() {
System.out.println("加糖");
}
}
制作咖啡
public class CoffeeDrink extends AbstractHotDrink{
@Override
protected void makeDrink() {
System.out.println("制作咖啡");
}
/**
* 想加果冻
*/
@Override
public void addCondiments() {
System.out.println("加果冻");
}
//当然你也可以覆盖customCondiments方法,return false则什么都不加
}
制作热茶
public class TeaDrink extends AbstractHotDrink{
@Override
protected void makeDrink() {
System.out.println("制作茶");
}
}
测试一波
钩子能够作为条件来进行控制判断是否要去做某事。通过模板设计模式,后续新增热豆浆、热牛奶等等都非常方便
模板设计模板在SpringSecurity源码中的应用
对于使用过SpringSecurity的小伙伴肯定对UsernamePasswordAuthenticationFilter非常熟悉,在ProviderManager#authenticate()中调用
然后调用AbstractUserDetailsAuthenticationProvider#authenticate
retrieveUser交由子类去实现,这就是一个典型的模板设计模式的体现
总结:
优点:
1、模板方法模式在定义了一组算法,将具体的实现交由子类负责。
2、模板方法模式是一种代码复用的基本技术。
3、模板方法模式导致一种反向的控制结构,通过一个父类调用其子类的操作,通过对子类的扩展增加新的行为,符合“开闭原则”。
缺点:
每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
使用场景
1、 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
2、 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。
3、控制子类的扩展
注意事项:为防止恶意操作,防止子类改变算法的实现步骤,一般模板方法都加上 final 关键词。