本文主要介绍模板方法设计模式的定义、作用及使用场景
引言
在软件开发中,设计模式是解决常见问题的经过验证的解决方案。模板方法设计模式,作为行为型设计模式的一种,提供了一种在不牺牲灵活性的前提下定义算法框架的方法。
本文将深入探讨模板方法设计模式,包括其定义、结构、应用场景、优缺点以及实际案例分析。
定义
模板方法模式在一个方法中定义了一个算法框架,将一些步骤延迟到子类中实现。它使得子类可以在不改变算法结构的情况下,重新定义算法的某些特定步骤。
模式结构
模板方法模式通常包含以下角色:
- 抽象类(Abstract Class):定义了模板方法和一些基本方法(包括钩子方法和抽象方法)。
- 具体类(Concrete Class):继承自抽象类并实现抽象方法。
public abstract class AbstractClass{
public final void templateMethod(){
// ... do something
method1();
// ... do something
method2();
// ... do something
}
protected abstract void method1();
protected abstract void method2();
}
public class ActionClass1 extends AbstractClass{
@Override
protected void method1(){
// ... do something
}
@Override
protected void method2(){
// ... do something
}
}
public class ActionClass2 extends AbstractClass{
@Override
protected void method1(){
// ... do something
}
@Override
protected void method2(){
// ... do something
}
}
AbstractClass demo = new ActionClass1();
demo.templateMethod();
作用
复用
在模板方法中,将可变的部分留给子类去实现,所有的子类都可以复用
父类中模板方法所定义的流程代码。
比如最常见的 InputStream
,就用到了模板方法设计模式。
public abstract class InputStream implements Closeable {
//...省略其他代码...
/**
模板方法,定义了读取数据的整个流程,且暴露了一个由子类去实现的抽象方法read()
*/
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
public abstract int read() throws IOException; //子类去实现
}
public class ByteArrayInputStream extends InputStream {
//...省略其他代码...
@Override
public synchronized int read() {
return (pos < count) ? (buf[pos++] & 0xff) : -1;
}
}
扩展
模板方法设计模式常用在框架的开发中,让框架的使用者可以在不修改框架源码的情况下,定制框架的功能。
比如HttpServlet
的service()
方法就是一个模板方法,它实现了整个Http请求的执行流程,doGet
和doPost
是模板中可以由子类来定制的部分。
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) {
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
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 {
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);
}
}
优点
- 高度的代码复用:通过共享不变的算法骨架,减少代码重复。
- 灵活性与扩展性:子类可以在不改变算法结构的前提下,提供特定步骤的实现。
- 松耦合:算法的骨架与具体实现解耦,提高了系统的可维护性。
应用场景
模板方法设计模式适用于以下场景:
- 多步骤算法:需要执行一系列固定顺序的步骤,但某些步骤的具体实现可能不同。
- 相似行为的家族:一组类具有相似的行为,但具体实现有所不同。
- 需要控制扩展:希望控制子类的扩展,同时保持算法结构的一致性。
实战
关于实战内容,我将在后续的文章中来详述一下,我当时怎么利用模板方法设计模式+责任链设计模式
来优化系统性能的。
今天,我只简单的聊一下,当时的背景是怎样的,为什么要进行系统优化。
背景
当时我负责的那个系统是财务系统,对财务熟悉的人应该知道,财务系统特别复杂,且在月初的时候需要进行账务核算。
在进行账务核算的时候,需要执行 10 个步骤,每一个步骤都是去登录不同的平台去执行。
- 登录应用服务器 1,执行 curl 命令;
- 登录数据库服务器,执行 sql;
- 登录应用服务器 2,执行 curl 命令;
- 用 postman 调用 python 接口。
- …
- …
只有当这 10 个步骤串行执行成功后,整个结账流程才算结束。
最开始负责这部分的同事,纯手工执行这 10 个步骤的,麻烦不说且十分耗时。
等我接手之后,对此进行了改造。
我们认真分析一下可以看出来,从步骤 1 到步骤 10 的执行,这不就很符合调度链
吗,而且每一个步骤的执行都需要依赖前一个步骤执行成功且自己是未执行的状态,这不就是一个模板
吗。
更详细的内容,我将作为一个单独的文章,更详细的展示出来。
结论与最佳实践
模板方法设计模式是一种强大的设计工具,它通过定义算法的骨架,提供了一种灵活而一致的方式来扩展系统。
在实际应用中,应该仔细考虑何时使用模板方法模式,以及如何设计抽象类和具体类,以确保系统的可维护性和可扩展性。