目录与学习目标
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-patternGit下载地址:https://github.com.cnpmjs.org/hesuijin/hesujin-design-pattern.gitdemo-study模块 下 behavior_design_pattern Template包