折腾Java设计模式之模板方法模式
博客原文地址:折腾Java设计模式之模板方法模式
模板方法模式
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
翻译过来就是,把算法的框架定义好,可以将某些步抽象出来放到子类去实现。模板方法允许子类在不改变算法框架的情况下重新实现算法的某些步骤。
模板方法模式UML图
UML图
抽象类AbstractClass定义了算法框架templateMethod()方法,其中有2个方法primitve1()和primitve2()被抽象出来,子类SubClass1继承了抽象类AbstractClass,从而实现了primitve1()和primitve2()。
模板方法模式角色
抽象类(AbstractClass): 定义了算法核心框架,同时把局部的算法行为封装成步骤,让子类去实现。
子类(SubClass): 继承了抽象类,实现抽象类中的抽象方法,具体实现了算法部分逻辑。
模板方法模式源码示例
源码地址:Template-method
抽象方法
先定义抽象类,抽象类AbstractProcessor中核心算法handle方法中大体分3部,第一先校验参数具体怎么校验放在子类中实现,第二获取结果也放在子类实现,第三获取结果后的操作也放在子类实现。
@Slf4j
public abstract class AbstractProcessor<P extends Request, R extends Response> {
/**
* 逻辑处理
*
* @param request
* @return
*/
public R handle(P request) {
// 1.校验参数
log.info("开始处理, 请求参数={}", request);
validRequest(request);
// 2.获取结果
R response = getResponse(request);
log.info("获取结果, 响应结果={}", response);
// 3.结果之后的处理
afterHandle(response);
return response;
}
/**
* 结果之后的处理 可以更新其他业务或者处理缓存
*
* @param response
*/
protected abstract void afterHandle(R response);
/**
* 校验请求参数
*
* @param request
*/
protected void validRequest(P request) {
if (Objects.isNull(request.getToken())) {
throw new RuntimeException("token不能为空");
}
if (Objects.isNull(request.getVersion())) {
throw new RuntimeException("version不能为空");
}
validRequestParam(request);
}
/**
* 校验请求真正的参数
* @param request
*/
protected abstract void validRequestParam(P request);
/**
* 获取到结果
* @param request
* @return
*/
protected abstract R getResponse(P request);
}
基本请求
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class Request {
private String version;
private String token;
}
基本响应
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class Response {
private String msg;
private int code;
private boolean success;
}
子类实现
第一个子类实现
@Slf4j
public class OneProcessor extends AbstractProcessor<OneRequest, OneResponse> {
public OneProcessor() {
ProcessorFactory.putProcess("two", this);
}
@Override
protected void afterHandle(OneResponse response) {
log.info("处理One结果: {}", response.getOne());
}
@Override
protected void validRequestParam(OneRequest request) {
log.info("校验one参数...省略......");
}
@Override
protected OneResponse getResponse(OneRequest request) {
String name = request.getName();
return OneResponse.builder()
.one(name + "one")
.success(true)
.code(0)
.msg("成功")
.build();
}
}
第一个子类的请求
@Data
@ToString(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class OneRequest extends Request {
private String name;
@Builder.Default
private int a = 0;
}
第一个子类的响应
@Data
@ToString(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class OneResponse extends Response {
private String one;
}
第二个子类实现
@Slf4j
public class TwoProcessor extends AbstractProcessor<TwoRequest, TwoResponse> {
public TwoProcessor() {
ProcessorFactory.putProcess("two", this);
}
@Override
protected void afterHandle(TwoResponse response) {
log.info("处理结果TWO, {}", response);
}
@Override
protected void validRequestParam(TwoRequest request) {
log.info("校验TWO参数...省略......");
}
@Override
protected TwoResponse getResponse(TwoRequest request) {
Long id = request.getId();
return TwoResponse.builder()
.two("id为"+id)
.success(true)
.code(0)
.msg("成功")
.build();
}
}
第二个子类的请求
@Data
@ToString(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class TwoRequest extends Request {
private Long id;
}
第二个子类的响应
@Data
@ToString(callSuper = true)
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class TwoResponse extends Response {
private String two;
}
扩展为工厂
有的时候我们定义的子类在Spring容器的时候由Spring定义好后,我们其实可以借用工厂模式方法,在子类初始化的时候就把子类放置在ProcessorFactory中,后续只需要根据key从中拿取即可,实际项目中用这种方式还是比较多的。
public class ProcessorFactory {
private static Map<String, AbstractProcessor> factories = new HashMap();
public static void putProcess(String key, AbstractProcessor process) {
factories.put(key, process);
}
public static AbstractProcessor selectProcess(String key) {
return factories.get(key);
}
}
执行程序
@Slf4j
public class TemplateMethodApplication {
public static void main(String[] args) {
OneRequest oneRequest = OneRequest.builder()
.version("2312312")
.token("23423")
.name("张三")
.build();
new OneProcessor().handle(oneRequest);
log.info("--------------------------");
TwoRequest twoRequest = TwoRequest.builder()
.version("2312312")
.token("23423")
.id(23456L)
.build();
new TwoProcessor().handle(twoRequest);
}
}
结果
总体上来讲,抽象类中定义了算法的框架,然后把部分算法步骤抽象出来供子类实现,有的时候有些方法只有个别子类会去实现,我们可以在抽象类中实现为空实现,在有需要的子类中我们可以覆盖抽象类的实现,这样避免了所有的子类都去实现,其实不关心的话都是空实现了。本示例用到了lombok的@SuperBuilder特性,可能在下载完完整的代码后会出现编译错误,这是因为Lombok的插件暂时还不支持@SuperBuilder。
模板方法模式总结
模板方法经常和其他模式混合使用,比如工厂、策略等等。其实在Spring中大量使用了模板方法模式,其实也不光在Spring中,像平时jdbcTemplate或者RedisTemplate,像这种带有Template的。
优点 1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。
缺点 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
使用场景 1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。
注意事项 为防止恶意操作,一般模板方法都加上 final 关键词。
参考
欢迎关注我的微信公众号
折腾Java设计模式之模板方法模式的更多相关文章
- 折腾Java设计模式之建造者模式
博文原址:折腾Java设计模式之建造者模式 建造者模式 Separate the construction of a complex object from its representation, a ...
- 折腾Java设计模式之备忘录模式
原文地址:折腾Java设计模式之备忘录模式 备忘录模式 Without violating encapsulation, capture and externalize an object's int ...
- 折腾Java设计模式之状态模式
原文地址 折腾Java设计模式之状态模式 状态模式 在状态模式(State Pattern)中,类的行为是基于它的状态改变的.这种类型的设计模式属于行为型模式.在状态模式中,我们创建表示各种状态的对象 ...
- 折腾Java设计模式之访问者模式
博客原文地址:折腾Java设计模式之访问者模式 访问者模式 Represent an operation to be performed on the elements of an object st ...
- 折腾Java设计模式之命令模式
博客原文地址 折腾Java设计模式之命令模式 命令模式 wiki上的描述 Encapsulate a request as an object, thereby allowing for the pa ...
- Java设计模式应用——模板方法模式
所谓模板方法模式,就是在一组方法结构一致,只有部分逻辑不一样时,使用抽象类制作一个逻辑模板,具体是实现类仅仅实现特殊逻辑就行了.类似科举制度八股文,文章结构相同,仅仅具体语句有差异,我们只需要按照八股 ...
- Java设计模式之模板方法模式(Template)
前言: 我们在开发中有很多固定的流程,这些流程有很多步凑是固定的,比如JDBC中获取连接,关闭连接这些流程是固定不变的,变动的只有设置参数,解析结果集这些是根据不同的实体对象“来做调整”,针对这种拥有 ...
- java设计模式之模板方法模式
模板方法模式 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中. 模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤.通俗的说的就是有很多相同的步骤的,在某一些地方可能有一些差 ...
- 折腾Java设计模式之迭代器模式
迭代器模式 Provide a way to access the elements of an aggregate object sequentially without exposing its ...
随机推荐
- Linux 文件/文件夹无法删除问题解决方案
最近我们的服务器被黑客攻击,然后有些文件的属性被修改,导致我们无法删除病毒文件,同时采用 root 用户也无法删除,现在把解决方案记录下来. 普通删除 如果文件是当前用户的,那么使用 rm 命令就可以 ...
- MongoDB 小记
之前本人说过一款非关系型数据库的代表 Redis 的 < Redis 小记 >文章,觉得意犹未尽,今天就来介绍一款数据库 MongoDB ,先来看一下 MongoDB是一款基于分布式文件存 ...
- 关于MQ,你必须知道的
我走过最长的路是你的套路 女:二号男嘉宾,假如我们牵手成功后,你会买名牌包包给我吗? 男:那你会听话吗? 女:会 听话. 男:听话 咱不买! OK那么消息队列MQ有什么套路呢?(这个话题转换生硬度连我 ...
- 微信小游戏爆款秘笈 数据库MongoDB攻略篇
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯云数据库 TencentDB 发表于云+社区专栏 随着微信小游戏的爆发,越来越多开发者关注到MongoDB与小游戏业务的契合度. ...
- 游戏服务器h2engine架构优化和跨平台设计
H2engine的GitHub星星不知不觉已经破百了,也没有特意推广过,但是慢慢的关注的人越来越多.因为事情多,好久没有写东西了,前一段时间有了一些想法,把h2engine又更新了一下,感觉h2eng ...
- Git 下载代码简单说明
昨天看码云上有两个项目感觉很好, 可惜竟然不会Git 找了很久看了好多文档看他写的都好复杂啊! 在这我给写出来一点点 1.下载Git https://git-scm.com/download/win ...
- 设计模式 | 简单工厂模式(static factory method)
按理说应该把书全都看完一遍,再开始写博客比较科学,会有比较全面的认识. 但是既然都决定要按规律更新博客了,只能看完一个设计模式写一篇了. 也算是逼自己思考了,不是看完就过,至少得把代码自己都敲一遍. ...
- 将包含经纬度点位信息的Excel表格数据导入到ArcMap中并输出成shapefile
将包含经纬信息的Excel表格数据,导入到ArcMap中并输出成shapefile,再进行后面的操作.使用这种方法可以将每一个包含经纬信息的数据在ArcMap中点出来. 一.准备数据 新建Excel表 ...
- Android 里的adb命令
ADB的全称为Android Debug Bridge,就是起到调试桥的作用. adb调试手机需要把usb调试打开 Android studio模拟器有的也要把模拟器usb调试打开,工具要灵活运用, ...
- 解决git Failed to connect to 127.0.0.1 port xxxx: Connection refused
某天,用git拉取,提交代码的时候出现了git Failed to connect to 127.0.0.1 port xxxx: Connection refused的问题, 开始百度,看了一通.都 ...