首页 > 技术文章 > 设计模式-模板方法模式

leisurexi 2019-03-12 21:57 原文

    本篇文章主要介绍模板方法模式。

    模板方法模式:模板方法模式是类的行为模式。准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而度剩余的逻辑有不同的实现。这就是模板方法模式的用意。

    我们先看下结构图。

 

      AbstractClass是抽象类,其实也就是一抽象模板,定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。

      下面我们以大话设计模式一书中的试卷例子来简单实现以下模板方法模式。

      首先是试卷父类。

package com.lwx.template_method;

/**
 * Created with IntelliJ IDEA.
 * Description: 考题试卷
 * User: lwx
 * Date: 2019-03-11
 * Time: 22:39
 */
public abstract class TestPaper {

    private void testQuestion1(){
        System.out.println("杨过得到,后来给了郭靖,炼成倚天剑、屠龙刀的玄铁可能是:A.球磨铸铁 B.马口铁 C.高速合金钢 D。碳素纤维");
        System.out.println("学生答案:" + answer1());
    }

    private void testQuestion2(){
        System.out.println("杨过、程英、陆无双铲除了情花造成:A.使这种植物不再害人 B.使一种珍稀物种灭绝 C.破坏了那个生物圈的生态平衡 D.造成该地区沙漠化");
        System.out.println("学生答案:" + answer2());
    }

    private void testQuestion3(){
        System.out.println("蓝凤凰致使华山师徒、核谷六仙呕吐不止,如果你是大夫,会给他们开什么药:A.阿匹斯林 B.牛黄解毒片 " +
                "C.氟哌酸 D.让他们喝大量的生牛奶 E.以上全不对");
        System.out.println("学生答案:" + answer3());
    }

    public void answerQuestion() {
        testQuestion1();
        testQuestion2();
        testQuestion3();
    }

    abstract String answer1();

    abstract String answer2();

    abstract String answer3();
}

    将答案写成抽象方法,让子类去实现。

    然后看下学生A子类。

package com.lwx.template_method;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: lwx
 * Date: 2019-03-11
 * Time: 22:49
 */
public class StudentAPaper extends TestPaper {

    @Override
    String answer1() {
        return "B";
    }

    @Override
    String answer2() {
        return "A";
    }

    @Override
    String answer3() {
        return "C";
    }
}

    只要继承试卷类,重写下答案方法即可,学生B也一样。

package com.lwx.template_method;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: lwx
 * Date: 2019-03-11
 * Time: 22:49
 */
public class StudentBPaper extends TestPaper {
    @Override
    String answer1() {
        return "C";
    }

    @Override
    String answer2() {
        return "B";
    }

    @Override
    String answer3() {
        return "A";
    }
}

    然后我们看下测试类和运行结果。

package com.lwx.template_method;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: lwx
 * Date: 2019-03-11
 * Time: 22:00
 */
public class Test {

    public static void main(String[] args) {
        TestPaper testPaper = null;

        System.out.println("学生A答题");
        testPaper = new StudentAPaper();
        testPaper.answerQuestion();

        System.out.println("----------------------------");

        System.out.println("学生B答题");
        testPaper = new StudentBPaper();
        testPaper.answerQuestion();
    }

}

    由于试卷是一样的,所以完全可以抽象成父类,然后在子类中去填写答案,这样就是通过把不变的行为搬移到超类,去除子类中的重复代码,这也是模板方法的特点。

    使用过Servlet的人都清楚,需要继承一个叫HttpServlet的抽象类。HttpServlet类提供了一个service()方法,这个方法调用七个do方法中的一个或几个,完成对客户端调用的响应。这些do方法需要由HttpServlet的具体子类提供,因此这是典型的模板方法模式。下面是service()方法的源代码。

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 = req.getDateHeader("If-Modified-Since");
                if (ifModifiedSince < lastModified) {
                    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);
        }

    }

    下面我们看下HttpServlet的结构图,并写一个简单的Servlet例子。

package com.lwx.template_method.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: lwx
 * Date: 2019-03-11
 * Time: 22:05
 */
@WebServlet(name = "myServlet", urlPatterns = "/myServlet")
public class MyServlet extends HttpServlet {

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().write("hello, world");
    }

}

    这里使用@WebServlet注解就不用在web.xml配置了,具体的内容可以看这篇博客 https://blog.csdn.net/mytt_10566/article/details/70173007,我们重写了doGet和doPost,由于是简单示例doPost我就不写了,只在doGet返回 hello,world,最后我们用tomcat启动工程。

    

    模板方法的优点和缺点:

    优点:

      1.模板方法提供了一个很好的代码复用平台。

      2.实现了反向控制,通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制,符合 "开闭原则"。

    缺点:

      1.每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。

 

    最后附上demo的githup地址:https://github.com/yijinqincai/design_patterns

 

推荐阅读