目录与学习目标
1:模板方式解决的问题(先了解)
2:模板模式的原理与实现
3:模板模式核心代码
4:模板模式复用例子(InputStream)
5:模板模式复用例子(AbstractList)
6:模板模式扩展例子(Junit)
7:模板模式扩展例子(Servlet)
1:模板方式解决的问题(先了解)
主要是用来解决复用和扩展两个问题
1:模板抽象类中的方法被实现类复用
2:不是指代码的扩展性,而是指框架的扩展性,有点类似我们之前讲到的控制反转
框架用户可以在不修改框架源码的情况下,定制化框架的功能
2:模板模式的原理与实现
模板方法模式在一个方法中定义一个逻辑骨架,并将某些步骤推迟到子类中实现。
模板方法模式可以让子类在不改变逻辑整体结构的情况下,重新定义逻辑中的某些步骤。
3:模板模式核心代码
1:模板方法类 (抽象类)
2:ConcreteClassOne ConcreteClassTwo 继承 模板方法类(抽象类)
3:调用方 直接 调用对应子类的 模板方法
4:模板方法 里面 依次执行对应方法
public abstract class AbstractClassTemplate {
//使用templateMethod方法依次执行对应方法
//使用final避免子类重写
public final void templateMethod() {
methodOne();
methodTwo();
}
protected abstract void methodOne();
protected abstract void methodTwo();
}
public class ConcreteClassOne extends AbstractClassTemplate {
@Override
protected void methodOne() {
System.out.println("我是ConcreteClassOne 的 methodOne()");
}
@Override
protected void methodTwo() {
System.out.println("我是ConcreteClassOne 的 methodTwo()");
}
}
public class ConcreteClassTwo extends AbstractClassTemplate {
@Override
protected void methodOne() {
System.out.println("我是ConcreteClassTwo 的 methodOne()");
}
@Override
protected void methodTwo() {
System.out.println("我是ConcreteClassTwo 的 methodTwo()");
}
}
public class TemplateDemoStart {
public static void main(String[] args) {
AbstractClassTemplate concreteClassOne = new ConcreteClassOne();
concreteClassOne.templateMethod();
AbstractClassTemplate concreteClassTwo = new ConcreteClassTwo();
concreteClassTwo.templateMethod();
}
}
4:模板模式复用例子(InputStream)
Java IO 类库中,有很多类的设计用到了模板模式,
比如 InputStream、OutputStream、Reader、Writer。我们拿 InputStream 来举例说明一下。
InputStream 部分相关代码贴在了下面。
在代码中,read() 函数是一个模板方法,定义了读取数据的整个流程,并且暴露了一个可以由子类来定制的抽象方法。
不过这个方法也被命名为了 read(),只是参数跟模板方法不同。
public abstract class InputStream implements Closeable {
//...省略其他代码...
//read 模板方法(带参数) 调用了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;
}
//read(不带参数 提供给实现类重写)
public abstract int read() throws IOException;
}
public class ByteArrayInputStream extends InputStream {
//...省略其他代码...
//实现类重写了 InputStream 的 read方法
@Override
public synchronized int read() {
return (pos < count) ? (buf[pos++] & 0xff) : -1;
}
}
5:模板模式复用例子(AbstractList)
在 Java AbstractList 类中,addAll() 函数可以看作模板方法,add() 是子类需要重写的方法。
尽管没有声明为 abstract 的,但子类不重写是不能使用的。
因为该方法实现直接抛出了 UnsupportedOperationException 异常。
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();
}
6:模板模式扩展例子(Junit)
Junit 框架也通过模板模式提供了一些功能扩展点(setUp()、tearDown() 等),让框架用户可以在这些扩展点上扩展功能。
在使用 JUnit 测试框架来编写单元测试的时候,我们编写的测试类都要继承框架提供的 TestCase 类。
在 TestCase 类中,runBare() 函数是模板方法:
它定义了执行测试用例的整体流程:先执行 setUp() 做些准备工作,然后执行 runTest() 运行真正的测试代码,最后执行 tearDown() 做扫尾工作。
尽管 setUp()、tearDown() 并不是抽象函数,还提供了默认的实现,不强制子类去重新实现,但这部分也是可以在子类中定制的,所以也符合模板模式的定义。
public abstract class TestCase extends Assert implements Test {
public void runBare() throws Throwable {
Throwable exception = null;
setUp();
try {
runTest();
} catch (Throwable running) {
exception = running;
} finally {
try {
tearDown();
} catch (Throwable tearingDown) {
if (exception == null) exception = tearingDown;
}
}
if (exception != null) throw exception;
}
/**
* Sets up the fixture, for example, open a network connection.
* This method is called before a test is executed.
*/
protected void setUp() throws Exception {
}
/**
* Tears down the fixture, for example, close a network connection.
* This method is called after a test is executed.
*/
protected void tearDown() throws Exception {
}
}
7:模板模式扩展例子(Servlet)
对于 Java Web 项目开发来说,常用的开发框架是 SpringMVC。
我们只需要关注业务代码的编写,底层的原理几乎不会涉及。但是,如果我们抛开这些高级框架来开发 Web 项目,必然会用到 Servlet。
使用比较底层的 Servlet 来开发 Web 项目也不难。我们只需要定义一个继承 HttpServlet 的类,
并且重写其中的 doGet() 或 doPost() 方法,来分别处理 get 和 post 请求。
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Hello World.");
}
}
同时需要在配置文件 web.xml 中做如下配置。
Tomcat、Jetty 等 Servlet 容器在启动的时候,会自动加载这个配置文件中的 URL 和 Servlet 之间的映射关系。
当我们在浏览器中输入网址(比如,http://127.0.0.1:8080/hello )的时候,
Servlet 容器会接收到相应的请求,并且根据 URL 和 Servlet 之间的映射关系,找到相应的 Servlet(HelloServlet),然后执行它的 service() 方法。
service() 方法定义在父类 HttpServlet 中,它会调用 doGet() 或 doPost() 方法,然后输出数据(“Hello world”)到网页。
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
HttpServlet 的 service() 方法就是一个模板方法,它实现了整个 HTTP 请求的执行流程,
doGet()、doPost() 是模板中可以由子类来定制的部分。
实际上,这就相当于 Servlet 框架提供了一个扩展点(doGet()、doPost() 方法),
让框架用户在不用修改 Servlet 框架源码的情况下,将业务代码通过扩展点镶嵌到框架中执行。
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
service(request, response);
}
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) {
// 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 {
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);
}
}
项目连接
请配合项目代码食用效果更佳:
项目地址:
https://github.com/hesuijin/hesujin-design-pattern
Git下载地址:
https://github.com.cnpmjs.org/hesuijin/hesujin-design-pattern.git
demo-study模块 下 behavior_design_pattern Template包