Java设计模式-模板方式模式
模板方法模式: 定义一个操作中的算法的骨架, 而将一些步骤延迟到子类中. 模板方法使得子类可以在不改变一个算法的结构的前提下重定义该算法的某些特定步骤.
(图片来源: 设计模式:可复用面向对象软件的基础)
Tips
处理某个流程的骨架代码已经具备, 但其中某节点的具体实现暂不确定, 此时可采用模板方法, 将该节点的代码实现转移给子类完成. 即: 处理步骤在父类中定义好, 具体实现延迟到子类中定义.
模式实现
到ATM取款机办理业务, 都会经过插卡、输密码、处理业务、取卡 等几个过程, 而且这几个过程一定是顺序执行的, 且除了 处理业务 (如取款、改密、查账) 可能会有所不同之外, 其他的过程完全相同. 因此我们就可以参考模板方法模式把插卡、输密码、取卡 3个过程放到父类中实现, 并定义一个流程骨架, 然后将 处理业务的具体逻辑 放到子类中:
AbstractClass 抽象模板:
定义抽象的原语操作,具体的子类将重定义它们以实现一个算法的各步骤.
实现一个模板方法,定义一个算法的骨架. 该模板方法不仅调用原语操作,也调用定义在AbstractClass或其他对象中的操作.
/**
* @author jifang
* @since 16/8/21 上午10:35.
*/
public abstract class AbstractATMBusiness {
public void run() {
System.out.println("-> 插卡");
System.out.println("-> 输入并校验密码");
if (checkPassword()) {
onBusiness();
}
System.out.println("-> 取卡");
}
// 具体业务处理延迟到子类实现
protected abstract void onBusiness();
private boolean checkPassword() {
// TODO Encode Password, Select DB & Comparison
return true;
}
}
AbstractATMBusiness是一个模板方法, 它定义了ATM操作的一个主要步骤并确定他们的先后顺序, 但允许子类改变这些具体步骤以满足各自的需求.
ConcreteClass
实现原语操作以完成算法中与特定子类相关的步骤; 每个AbstractClass都可有任意多个ConcreteClass, 而每个ConcreteClass都可以给出这些抽象方法的不同实现, 从而使得顶级逻辑的功能各不相同:
class CheckOutConcreteATMBusiness extends AbstractATMBusiness {
@Override
protected void onBusiness() {
System.out.println(" ... 取款");
}
}
class ChangePasswordConcreteATMBusiness extends AbstractATMBusiness {
@Override
protected void onBusiness() {
System.out.println(" ... 修改密码");
}
}
Client
/**
* Created by jifang on 15/12/3.
*/
public class Client {
@Test
public void client() {
AbstractATMBusiness changePassword = new ChangePasswordConcreteATMBusiness();
changePassword.run();
AbstractATMBusiness checkOut = new CheckOutConcreteATMBusiness();
checkOut.run();
}
}
实例
Servlet
HttpServlet定义了service()方法固定下来HTTP请求的整体处理流程,使得开发Servlet只需继承HttpServlet并实现doGet()/doPost()等方法完成业务逻辑处理, 并不需要关心具体的HTTP响应流程:
/**
* HttpServlet中的service方法
*/
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
详见: Servlet - 基础.
统一定时调度
将这个示例放在此处可能有些不大合适, 但它也体现了一些模板方法的思想:
1. 实现
ScheduleTaskMonitor
/**
* @author jifang
* @since 16/8/23 下午3:35.
*/
public class ScheduleTaskMonitor implements InitializingBean, DisposableBean {
private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleTaskMonitor.class);
private static final int _10S = 10_000;
private List<ScheduleTask> tasks = new CopyOnWriteArrayList<>();
private static final Timer timer = new Timer("ScheduleTaskMonitor");
private void start() {
timer.schedule(new TimerTask() {
@Override
public void run() {
for (ScheduleTask task : tasks) {
task.scheduleTask();
}
}
}, 0, _10S);
}
public void register(ScheduleTask task) {
tasks.add(task);
}
@Override
public void afterPropertiesSet() throws Exception {
this.start();
LOGGER.info("Start Monitor {}", this.getClass());
}
@Override
public void destroy() throws Exception {
timer.cancel();
LOGGER.info("Stop Monitor {}", this.getClass());
}
}
ScheduleTask
public interface ScheduleTask {
void scheduleTask();
}
2. 使用
只需在spring的配置文件中引入该Bean:
<bean id="monitor" class="com.template.ScheduleTaskMonitor"/>
需要统一定时的类实现ScheduleTask接口, 并将自己注册到monitor中:
/**
* @author jifang
* @since 16/3/16 上午9:59.
*/
@Controller
public class LoginController implements ScheduleTask, InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class);
@Autowired
private ScheduleTaskMonitor monitor;
@Override
public void scheduleTask() {
LOGGER.error("O(∩_∩)O 日志记录~");
}
@Override
public void afterPropertiesSet() throws Exception {
monitor.register(this);
}
}
即可完成scheduleTask()方法的定时调度.
小结
模板方法模式提供了一个很好的代码复用平台, 他通过把不变行为搬移到父类, 去除子类中重复代码来体现它的优势: 有时我们会遇到由一系列步骤构成的过程需要执行, 该过程从高层次上看是相同的, 但有某些细节的实现可能不同, 此时就可以考虑使用用模板方法了.
适用
一次性实现算法的不变部分, 并将可变的行为留给子类来实现;
各子类中公共的行为应该被提取出来并集中到一个公共父类中避免代码重复, 如: Servlet 的 service()方法.
控制子类扩展, 模板方法只在特定点调用hook操作, 这样就只允许在这些点进行扩展, 如: Junit测试框架.
相关模式
Factory Method常被模板方法调用.
Strategy: 模板方法使用继承来改变算法的一部分, Strategy使用委托来改变整个算法.
- GMP大法教你重新做人(从入门到实战)
- Highway Networks
- CTF---编程入门第一题 循环
- Z.ExtensionMethods 一个强大的开源扩展库
- 【干货】神经网络SRU
- AutoMapper 使用实践
- CTF---安全杂项入门第二题 A记录
- PyTorch(总)---PyTorch遇到令人迷人的BUG与记录
- 手把手带你进入TOP20的商超销售预测
- 【干货荟萃】机器学习&深度学习知识资料大全集(二)(论文/教程/代码/书籍/数据/课程等)
- 逆天通用水印扩展篇~新增剪贴板系列的功能和手动配置,卸除原基础不常用的功能
- 【专知-Java Deeplearning4j深度学习教程06】用卷积神经网络CNN进行图像分类
- 万恶的剪贴板==》为存储而生
- AdaBoost算法(R语言)
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- maven之第一个maven程序
- 【LeetCode】重建二叉树day04
- 【LeetCode】从尾到头反过来返回每个节点的值(用数组返回)day03
- mybatis文件映射之利用association进行关联查询(二)
- 【JUC】CountDownLatch你真的了解吗?
- mybatis动态sql之foreach补充(一)
- 【LeetCode】把字符串 s 中的每个空格替换成““%20””day02
- bert加载数据代码
- python爬虫--看看虎牙女主播中谁颜值最高
- 基于maven+ssm的增删改查之maven环境的搭建
- (22)Bash环境变量
- maven之在eclipse中创建maven项目
- wiki百科之将词转换为索引表示
- 【LeetCode】找出数组中重复的数字day01
- pytorch实现的transformer代码分析