学习设计模式——模板模式

今天,我们来学习行为型设计模式中的第二个:模板模式

模板模式主要是用来解决复用扩展 两个问题。

话不多说,开始今天的学习。

介绍

模板模式(Template Method Design Pattern): 模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。

这里的算法骨架就是“模板”,通用的代码实现很简单。

原理与实现

AbstracTemplate.java

1
2
3
4
5
6
7
8
9
10
11
java复制代码public abstract class AbstracTemplate {

public final void templateMethod() {
methodA();
methodB();
}

protected abstract void methodA();
protected abstract void methodB();

}

ConcreteClassA.java、ConcreteClassB.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
java复制代码public class ConcreteClassA extends AbstracTemplate {
@Override
protected void methodA() {
System.out.println("执行了 ConcreteClassA 的方法 methodA");
}

@Override
protected void methodB() {
System.out.println("执行了 ConcreateClassA 的方法 methodB");
}
}


public class ConcreteClassB extends AbstracTemplate {

@Override
protected void methodA() {
System.out.println("执行了 ConcreteClassB 的方法 methodA");
}

@Override
protected void methodB() {
System.out.println("执行了 ConcreteClassB 的方法 methodB");
}
}

Test.java

1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码public class Test {

public static void main(String[] args) {

AbstracTemplate classA = new ConcreteClassA();
classA.templateMethod();
System.out.println("===============================");
AbstracTemplate classB = new ConcreteClassB();
classB.templateMethod();

}

}

结果:

image.png

作用一:复用

模板模式的作用之一:复用

模板模式把一个算法中不变的流程抽象到父类的模板方法 templateMethod()中,将可变的部分 methodA()、methodB() 留给子类 ConcreteClassA 和 ConcreteClassB 来实现。所有的子类都可以复用父类中模板方法定义的流程代码。

Java AbstractList

在 Java AbstractList 类中,addAll() 函数可以看作模板方法,add() 是子类需要重写的方法,尽管没有声明为 abstract 的,但函数实现直接抛出了 UnsupportedOperationException 异常。前提是,如果子类不重写是不能使用的。

1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
boolean modified = false;
for (E e : c) {
add(index++, e);
modified = true;
}
return modified;
}

public void add(int index, E element) {
throw new UnsupportedOperationException();
}

还有在Java IO 类库汇总,有很多的类的设计也用到了模板模式,比如 InputStream、OutputStream、Reader、Writer。

作用二:扩展

模板模式的作用之二:扩展。

基于这个作用,模板模式常用在框架的开发中,让框架用户可以在不修改框架源码的情况下,定制化框架的功能。
还记得当初学 Java Web 开发时,必然会学到 Servlet。使用 Servlet 来开发 Web 项目时,我们需要定义一个继承 HttpServlet 的类,并且重写其中的 doGet() 或 doPost() 方法,来分别处理 get 和 post 请求。具体代码示例如下所示:

1
2
3
4
5
6
7
8
9
10
11
java复制代码public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Hello World!");
}
}

当请求到这个 Servlet 的时候会执行 它的 service() 方法。service() 方法定义在父类 HttpServlet 中,它会调用 doGet() 或 doPost() 方法,然后输出数据 “Hello world” 到网页。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
java复制代码protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}

if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}

}

可以看到,service() 方法就是一个模板方法,它实现了整个 HTTP 请求的执行流程,doGet()、doPost() 是模板中可以由子类来定制的部分。实际上,这就相当于 Servlet 框架提供了一个扩展点(doGet()、doPost() 方法),让框架用户在不用修改 Servlet 框架源码的情况下,将业务代码通过扩展点镶嵌到框架中执行。

总结

模板模式的优点:

  • 具体细节步骤实现定义在子类中,子类定义详细处理算法是不会改变算法整体结构。
  • 代码复用的基本技术,在数据库设计中尤为重要。
  • 存在一种反向的控制结构,通过一个父类调用其子类的操作,通过子类对父类进行扩展增加新的行为,符合“开闭原则”。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%