13、设计模式之模板模式(Template)

一、什么是模板模式

模板模式是一种基于继承实现的设计模式,它是行为型的模式。

主要思想是将定义的算法抽象成一组步骤,在抽象类种定义算法的骨架,把具体的操作留给子类来实现。

通俗地说,模板模式就是将某一行为制定一个框架,然后子类填充细节。比如说做菜,流程通常就是洗菜、切菜、炒菜等步骤,那么这个流程就可以看作是一个模板,而具体做什么菜由子类来实现。

二、角色组成

抽象类(Abstract):定义了算法骨架,包含一个或多个抽象方法,这些方法由子类来具体实现。抽象类中通常还包含一个模板方法,用来调用抽象方法和具体方法,控制算法的执行顺序;还可以定义钩子方法,用于在算法中进行条件控制。

具体类(Concrete Class):继承抽象类,实现抽象方法。

三、优缺点

优点:

提高代码复用性:将算法的骨架定义在父类中,子类只需要实现具体的细节部分,减少了代码的重复。

符合开闭原则:在模板模式种,由父类控制子类的执行,通过子类对父类进行扩展增加新的行为,符合"开闭原则"。

提高代码可维护性:模板模式定义了一套固定的模板,便于开发人员理解和修改,易于维护。

缺点:

部分子类可能无法灵活定制:由于模板模式制定的是一个固定的结构,所以某些子类可能无法适用,导致无法实现特定的需求或定制。

类的数量增加:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。

四、应用场景

4.1 生活场景

写文章:假设我们要写一篇文章,其中包括标题、引言、正文和结论等部分。我们可以将文章的写作过程定义为一个模板,在抽象类中定义写作方法,如先写标题、再写引言、接着写正文,最后写结论。具体类就是每篇文章的实现类,它们可以根据主题和内容的不同来实现对应的部分。这样,每个具体文章类只需要关注自己特定的内容,而写作的步骤则由模板来控制

学习流程:比如学习某门课程的流程,在学习过程中有一些共同的步骤,比如预习、上课、复习做练习等。我们可以定义一个抽象类StudyCourse,在其中定义学习的方法。具体类就是每门具体课程的实现类,它们根据课程内容和学习方式来实现抽象类中的方法。模板方法则是定义在抽象类中的一组方法,用于规定学习的整体流程和一些基本规则。

4.2 java场景

JdbcTemplate:JdbcTemplate提供了一系列的模板方法,如execute、query、update等。开发者可以通过继承JdbcTemplate并实现相应的抽象方法来完成数据库操作的具体实现。

HttpServlet:HttpServlet类是一个抽象类,提供了handleRequest、doGet、doPost等模板方法,用于处理HTTP请求。Servlet开发者可以继承HttpServlet并实现这些方法来处理具体的请求,从而完成一个特定的Servlet实现。

Servlet过滤器:Java Servlet

API中提供了过滤器(Filter)接口,用于对Servlet请求进行拦截和处理。该接口中定义了一个doFilter()方法,该方法是一个模板方法,由子类实现具体的请求拦截和处理方式。

五、代码实现

下面以订外卖为例,解释一下模板模式。假设订外卖的过程包含三个步骤:选择外卖、支付、取外卖、是否打赏,我们可以定义一个OderFood的抽象类,那么选择外卖就可以是抽象方法,需要子类取实现它,支付和取外卖可以定义为具体方法,另外是否打赏为钩子方法,子类可以决定是否对算法的不同进行挂钩,还需要定义一个模板方法,用以控制流程;不同的商家,如KFC、星巴克就是具体类。

5.0 UML类图

5.1 OrderFood------抽象类(Abstract)

java 复制代码
/**
 * 
 * 1.抽象类(Abstract Class):点外卖
 * 包含选择外卖、支付、取外卖三个方法,
 * 其中选择外卖为抽象方法,需要子类实现
 * 支付、取外卖为具体方法
 * 另外是否打赏为钩子方法,子类可以决定是否对算法的不同进行挂钩
 */
public abstract class OrderFood {
 
    //模板方法
    public void order(){
        selectFood();
        pay();
        getFood();
    }
    //选择外卖   抽象方法 由子类实现具体细节
    public abstract void selectFood();
    //是否打赏   钩子方法 可以重写来做条件控制
    public boolean isGiveAward(){
        return false;
    }
    //-------具体方法----------
    public void pay(){
        System.out.println("支付成功,外卖小哥正在快马加鞭~~");
    }
 
    //取外卖
    public void getFood(){
        System.out.println("取到外卖");
        if (isGiveAward()){
            System.out.println("打赏外卖小哥");
        }
    }
}

5.2 具体类(Concrete Class)

java 复制代码
/**
 * 
 * 具体类(Concrete Class):星巴克
 */
public class Starbucks extends OrderFood{
 
    //实现父类方法
    @Override
    public void selectFood() {
        System.out.println("一杯抹茶拿铁");
    }
 
    //重写钩子方法,打赏外卖小哥
    @Override
    public boolean isGiveAward(){
        return true;
    }
}
/**
 * 
 * 具体类(Concrete Class):KFC
 */
public class KFC extends OrderFood{
    @Override
    public void selectFood() {
        System.out.println("一份汉堡炸鸡四件套");
    }
}

5.3 testTemplate

java 复制代码
/**
 * 
 * 模板模式测试类
 */
@SpringBootTest
public class TestTemplate {
 
    @Test
    void testTemplate(){
        //星巴克(重写了钩子方法,打赏外卖小哥)
        OrderFood orderFood=new Starbucks();
        orderFood.order();
        System.out.println("--------KFC------------");
        //KFC
        OrderFood orderFood1=new KFC();
        orderFood1.order();
    }
}

六、总结

我们可以把模板看作是一个公共的蓝本,而子类就像是根据这个蓝本来实现自己独特的需求。所以当我们在开发中遇到一些情况,比如多个类共享一些相同的操作,或者说想要控制子类扩展某个算法的一部分功能时,就可以考虑模板模式了。另外,还有以下几个适用场景以做参考:

子类需要扩展算法的部分功能:当需要控制子类对算法的某些步骤进行扩展或修改,同时保持算法骨架不变时,可以使用模板模式。

实现多个算法的不同细节:当需要在不同的场景下使用相同的算法,但是细节实现不同,可以使用模板模式。

需要统一流程的业务:在一些对流程敏感的业务中,例如订餐、下单等,使用模板方法模式可以统一流程,使代码更加简洁和易维护。并且在统一流程的同时,也能保证业务的正确性。

注意事项: 为防止恶意操作,一般模板方法都加上 final 关键词。

相关推荐
计算机学姐1 分钟前
基于SpringBoot+Vue的在线投票系统
java·vue.js·spring boot·后端·学习·intellij-idea·mybatis
penguin_bark7 分钟前
69. x 的平方根
算法
这可就有点麻烦了17 分钟前
强化学习笔记之【TD3算法】
linux·笔记·算法·机器学习
救救孩子把17 分钟前
深入理解 Java 对象的内存布局
java
落落落sss19 分钟前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
苏宸啊22 分钟前
顺序表及其代码实现
数据结构·算法
万物皆字节25 分钟前
maven指定模块快速打包idea插件Quick Maven Package
java
lin zaixi()26 分钟前
贪心思想之——最大子段和问题
数据结构·算法
FindYou.26 分钟前
C - Separated Lunch
算法·深度优先
夜雨翦春韭32 分钟前
【代码随想录Day30】贪心算法Part04
java·数据结构·算法·leetcode·贪心算法