设计模式-行为型模式-模板方法模式
一、什么是模板模式
模板方法模式(Template Method Pattern)是一种行为型设计模式,它定义了一个算法骨架,允许子类在不改变算法整体结构的情况下重新定义算法的某些步骤。
主要组成部分:
1、模板方法(Template Method): 定义了算法的骨架结构,包含了一个算法的整体流程,并将某些步骤的实现推迟到子类中。通常作为一个抽象方法或者具有默认实现的方法。
2、具体方法(Concrete Methods): 模板方法中的步骤可能有一些默认实现,在模板方法中直接实现的步骤。
3、抽象方法(Abstract Methods): 在模板方法中留给子类实现的方法,用于子类实现具体步骤。
工作原理:
模板方法模式定义了一个固定算法的框架,其中一些步骤是固定的,而另一些可以由子类实现。抽象类中的模板方法规定了算法的结构和执行顺序,而具体的步骤则由子类实现。
二、场景模拟
这里拿2023年海康威视秋招的一个问答题为例。题目是这么描述的:实现两个巡检任务,其中A巡检任务使用现场扫描二维码的方式巡检机房的服务器状态(正常/异常),B巡检任务使用远程视频方式巡检服务器正常运行率(百分比数字),两个巡检任务结束后都要提交巡检结果。使用模板方法设计模式解决。
我们简单的将A和B任务的流程画出了,很简单,就是A使用二维码方式巡检,返回正常/异常的结果;B使用远程视频方式巡检,返回百分率。这里,我们发现,他们有着共同的流程,先巡检,再上报,可能有些方法的具体实现不一样,但是整体流程是确定的。
三、业务实现
其中抽象类Inspect定义了巡检的流程,然后提供了默认的实现方法 。两个不同的巡检方法继承Inspect,可以根据需求自定义方法,也可以使用Inspect提供的默认实现方法。
3.1、使用模板类定义一个巡检框架
@Slf4j
public abstract class Inspect<T> {
// 巡检名称
private final String inspectName;
public Inspect(String inspectName) {
this.inspectName = inspectName;
}
public T startInspect(){
initInspect();
inspectFunction();
return submitInspect();
}
/**
* 初始化巡检方法
*/
protected void initInspect(){
log.info("初始化巡查系统" + inspectName);
}
// 模拟不同的巡检方式
protected abstract void inspectFunction();
// 返回巡检信息
protected abstract T submitInspect();
}
可以看到上方定义了三个流程,首先初始化,然后模拟不同的巡检方式,最后返回巡检信息。流程是固定的,只不过不同的巡检可能使用不同的方式,所以需要写单独的类去实现。
3.2、子类继承模板抽象类后自定义
例如巡检A,使用的是二维码的形式巡检,并返回(正常/异常)状态,我们用布尔值模拟。
@Slf4j
public class AInspect extends Inspect<Boolean> {
public AInspect(String inspectName) {
super(inspectName);
}
@Override
protected void inspectFunction() {
log.info("使用二维码方式巡检……");
}
@Override
protected Boolean submitInspect() {
return true;
}
}
巡检B,使用的是远程视频监控的形式,并返回百分比比例,我们用double模拟;
@Slf4j
public class BInspect extends Inspect<Double> {
public BInspect(String inspectName) {
super(inspectName);
}
@Override
protected void inspectFunction() {
log.info("使用远程视频监控方式巡检……");
}
@Override
protected Double submitInspect() {
return 88.88d;
}
}
3.3、测试
public static void main(String[] args) {
Inspect<Boolean> inspectA = new AInspect("A任务");
Boolean resultA = inspectA.startInspect();
log.info("A巡视结果:{}", resultA);
Inspect<Double> inspectB = new BInspect("B任务");
Double resultB = inspectB.startInspect();
log.info("B巡视结果:{}", resultB);
}
可以看到运行结果,巡检系统A和B分别用不同的方式完成了巡检,且流程一致。
四、小结
模板方法定义了一个算法的骨架,将一些步骤推迟到子类去实现。如果在我们的项目中,有一个业务流程是固定的,但是实现业务的方式是多变的,我们可以考虑模板方法,来优化我们的代码结构。
可能有些人分不清楚模板方法和策略模式的区别,这里我区分一下。
首先就定义和结构方面:模板方法用抽象类定义了一系列的流程,子类继承他的流程,但是可以修改流程某个点的实现方式或者是继承父类的默认实现方式;策略模式是定义一个接口,多个类去实现这个接口,允许客户端在运行时选择使用的策略。
它们的目的也是不一样的:模板方式是确保各个步骤执行顺序,一定层面上减少重复代码;策略模式是让调用者自由的选择实现方式或行为,且多个实现方式之前可以替换。
区分谁是谁并不怎么重要,因为在一念之间,他们有可能会相互替换,需要看自己的业务需求。而且很多时候,这两种设计模式可能配合使用,实现拓展和定制,这里我举例一种情况。就拿策略模式这个来说,我这里只写了计算某种材料的计算策略,但是在实际的业务中,我们可能需要统计这一个月或者一年的材料成本量,就简单来说,我们需要算出这一个月的使用量,然后这一年的使用量是这12个月加起来的使用量。这个流程就大致为:计算出某一条记录的成本;计算出一个月的成本;计算出一年的成本。我们就可以定义一个模板方法,算不同材料的年成本。这里我们就可以嵌套进去我们的策略模式,让两者搭配使用,完成业务需求。当然两者配合的场景可能有很多,这里我举得例子可能不是那么恰当,但是也差不多这个意思。