1. 介绍
1.1 定义
- 模板方法模式(Template Pattern),又叫模板模式,它属于行为型模式
- 模板方法模式定义一个模板结构,将具体内容延迟到子类去实现
1.2 作用
- 使得子类可以在不改变一个模板的结构的前提下重新定义该模板的某些特定步骤
2. 模式结构
2.1 UML类图
2.2 模式组成
- 模板方法(Template Method):模板方法定义了模板的特定步骤
- 抽象方法(Abstract Method):抽象方法由抽象类声明,由其子类实现,使用abstract关键字标识
- 具体方法(Concrete Method):具体方法由抽象类声明并实现,子类不可修改,使用final关键字标识
- 钩子方法(Hook Method):钩子方法由抽象类声明,子类可以重写
3. 代码实例
3.1 背景
申请虚拟机需要分配ip,网卡,磁盘等,闲置时需要进行删除,每个操作的请求体各不相同,但是流程是一致的,比如参数校验,资源评估,生成任务id,提交任务等,这个时候定义一个模板类相当合适。
3.2 应用
代码主要为了说明模板方法模式的大致用法,非常简化,仅供参考
-
步骤1 定义request类(公共参数可以抽成父类)
public class AbstractVmRequest {
/**
* create/delete
*/
private String action;
private String operator;
private String jobId;
}
public class VmIpRequest extends AbstractVmRequest {
private List<CreateIpVO> createIpVOList;
private List<DeleteIpVO> deleteIpVOList;
}
public class VmVolumeRequest extends AbstractVmRequest {
private List<CreateVolumeVO> createVolumeVOList;
private List<DeleteVolumeVO> deleteVolumeVOList;
}
-
步骤2 定义抽象模板类
public abstract class AbstractVmRegister {
public final String VmRegister(AbstractVmRequest request) throws Exception {
// 参数校验
verifyParameter(request);
// 资源评估
evaluateResource(request);
// 生成任务id
String jobId = generateJobId(request);
// 提交并执行任务
submitVmJob(request);
return jobId;
}
// 抽象方法,由子类实现
protected abstract void verifyParameter(AbstractVmRequest request) throws Exception;
// 钩子方法,父类已实现,子类也可以重写
protected void evaluateResource(AbstractVmRequest request) {
// 当前资源充足,不进行资源评估
System.out.println("skip evaluate resource");
}
// 具体方法,父类已实现,子类无需实现
final String generateJobId(AbstractVmRequest request) {
String jobId = UUID.randomUUID().toString();
request.setJobId(jobId);
return jobId;
}
// 抽象方法,由子类实现
protected abstract void submitVmJob(AbstractVmRequest request);
}
-
步骤3 定义实现类-ip
public class VmIpCreateRegister extends AbstractVmRegister {
@Override
protected void verifyParameter(AbstractVmRequest request) throws Exception {
VmIpRequest vmIpRequest;
if (request instanceof VmIpRequest) {
vmIpRequest = (VmIpRequest) request;
} else {
throw new Exception("The bean type does not support ip create");
}
if (CollectionUtils.isEmpty(vmIpRequest.getCreateIpVOList())) {
throw new Exception("createIpVOList can not be empty");
}
}
@Override
protected void submitVmJob(AbstractVmRequest request) {
// 调用service方法执行操作
// ipService.execute()
System.out.println(String.format("submit ip create job, jobId: %s", request.getJobId()));
}
}
-
步骤4 定义实现类-volume
public class VmVolumeCreateRegister extends AbstractVmRegister {
@Override
protected void verifyParameter(AbstractVmRequest request) throws Exception {
VmVolumeRequest vmVolumeRequest;
if (request instanceof VmVolumeRequest) {
vmVolumeRequest = (VmVolumeRequest) request;
} else {
throw new Exception("The bean type does not support volume create");
}
if (CollectionUtils.isEmpty(vmVolumeRequest.getCreateVolumeVOList())) {
throw new Exception("createVolumeVOList can not be empty");
}
}
@Override
protected void submitVmJob(AbstractVmRequest request) {
// 调用service方法执行操作
// volumeService.execute()
System.out.println(String.format("submit volume create job, jobId: %s", request.getJobId()));
}
}
-
外部调用(这边推荐使用策略模式,提前将实现类放在map里,调用的时候直接通过map获取实现类)
public static void main(String[] args) throws Exception {
// 填充参数模拟api调用
VmVolumeRequest vmVolumeRequest = new VmVolumeRequest();
// action可以写成枚举类
vmVolumeRequest.setAction("create");
vmVolumeRequest.setOperator("DustHeart");
vmVolumeRequest.setCreateVolumeVOList(Collections.singletonList(new CreateVolumeVO()));
VmVolumeCreateRegister vmVolumeCreateRegister = new VmVolumeCreateRegister();
String jobId = vmVolumeCreateRegister.VmRegister(vmVolumeRequest);
System.out.println(jobId);
}
4. 优点
- 提高代码复用性,将相同处理逻辑的代码放在抽象父类中
- 提高了拓展性,子类实现抽象父类的某些细节,有助于模板父类的扩展
- 符合开闭原则,通过一个父类调用子类实现的操作,可以通过子类扩展增加新的行为
5. 缺点
- 引入了抽象类,每一个不同的实现都需要一个子类来实现,导致类的个数增加,系统会更加庞大
- 模板方法主要通过继承实现,如果父类增加新的抽象方法,所有的子类都要修改一遍
6. 应用场景
- 编排一个流程的固定步骤,实现某些步骤不变的部分,并将可变的步骤留给子类来实现,使得子类在步骤固定的情况下进行功能的不同实现和拓展