zoukankan      html  css  js  c++  java
  • 代码复用神器,模板模式实操分享

    一、介绍

    模板模式,顾名思义,定义一个模板,将部分逻辑以具体方法或者具体构造函数的形式实现,在抽象类中声明一些抽象方法来迫使子类实现剩余的逻辑。

    不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现,这就是模板方法模式的用意。

    模板模式涉及到三个角色:

    • 抽象类(AbstractClass):实现了模板方法,定义了算法的骨架;
    • 具体类(ConcreteClass):实现抽象类中的抽象方法,已完成完整的算法;
    • 客户角色:客户类提出使用具体类的请求;

    二、示例

    举个例子,以早上起床到上班所需要的操作为例,大致流程可以分为以下几步:穿衣服、刷牙、洗脸、吃早餐等。男生和女生的操作可能有些区别。

    我们创建一个抽象的类,定义好大致的操作流程,如下:

    /**
     * 抽象类
     */
    public abstract class AbstractPerson {

        /**
         * 定义操作流程
         */
        public void prepareGoWorking(){
            dressing();//穿衣服
            brushTeeth();//刷牙
            washFace();//洗脸
            eatBreakFast();//吃早餐
        }

        /**穿衣服*/
        protected abstract void dressing();

        /**刷牙*/
        protected void brushTeeth(){
            System.out.println("刷牙");
        }

        /**洗脸*/
        protected void washFace(){
            System.out.println("洗脸");
        }

        /**吃早餐*/
        protected abstract void eatBreakFast();

    }

    因为男生和女生的行为不一样,我们分别创建两个具体类,如下:

    /**
     * 男生
     * 具体实现类
     */
    public class ManPerson extends AbstractPerson{

        @Override
        protected void dressing() {
            System.out.println("穿西装");
        }

        @Override
        protected void eatBreakFast() {
            System.out.println("直接在公司吃早餐");
        }
    }
    /**
     * 女生
     * 具体实现类
     */
    public class WomanPerson extends AbstractPerson{

        @Override
        protected void dressing() {
            System.out.println("穿休闲衣服");
        }

        @Override
        protected void eatBreakFast() {
            System.out.println("在家弄点吃的,或者在外面买一点小吃");
        }
    }

    创建一个客户端,实现如下:

    public class TemplateClient {

        public static void main(String[] args) {
            //男生起床步骤
            ManPerson manPerson = new ManPerson();
            System.out.println("-----男生起床步骤----");
            manPerson.prepareGoWorking();
            System.out.println("-----女生起床步骤----");
            //女生起床步骤
            WomanPerson womanPerson = new WomanPerson();
            womanPerson.prepareGoWorking();
        }
    }

    输出结果:

    -----男生起床步骤----
    穿西装
    刷牙
    洗脸
    直接在公司吃早餐
    -----女生起床步骤----
    穿休闲衣服
    刷牙
    洗脸
    在家弄点吃的,或者在外面买一点小吃

    当然,模版模式的玩法,还不仅仅只有这些,还可以在模版模式中使用挂钩(hook)。

    什么是hook呢?存在一个空实现的方法,我们称这种方法为hook。子类可以视情况来决定是否要覆盖它。

    还是以上面为例子,比如吃完早餐就要出门上班,选择什么交通工具呢?

    抽象类新增方法hook(),内容如下:

    /**
     * 抽象类
     */
    public abstract class AbstractPerson {

        /**
         * 定义操作流程
         */
        public void prepareGoWorking(){
            dressing();//穿衣服
            brushTeeth();//刷牙
            washFace();//洗脸
            eatBreakFast();//吃早餐
            hook();//挂钩
        }

        /**穿衣服*/
        protected abstract void dressing();

        /**刷牙*/
        protected void brushTeeth(){
            System.out.println("刷牙");
        }

        /**洗脸*/
        protected void washFace(){
            System.out.println("洗脸");
        }

        /**吃早餐*/
        protected abstract void eatBreakFast();

        /**挂钩*/
        protected void hook(){};

    }

    男生具体实现类,重写hook()方法,内容如下:

    /**
     * 男生
     * 具体实现类
     */
    public class ManPerson extends AbstractPerson{

        @Override
        protected void dressing() {
            System.out.println("穿西装");
        }

        @Override
        protected void eatBreakFast() {
            System.out.println("直接在公司吃早餐");
        }

        @Override
        protected void hook() {
            System.out.println("乘地铁上班");
        }
    }

    运行测试类,男生具体实现类,输出结果:

    -----男生起床步骤----
    穿西装
    刷牙
    洗脸
    直接在公司吃早餐
    乘地铁上班

    当然,还有其他的玩法,比如女生洗完脸之后,可能需要化妆,我们再次将抽象类进行处理,内容如下:

    /**
     * 抽象类
     */
    public abstract class AbstractPerson {

        /**
         * 定义操作流程
         */
        public void prepareGoWorking(){
            dressing();//穿衣服
            brushTeeth();//刷牙
            washFace();//洗脸
            //是否需要化妆,默认不化妆
            if(isMakeUp()){
                System.out.println("进行化妆");
            }
            eatBreakFast();//吃早餐
            hook();//挂钩
        }

        /**是否需要化妆方法*/
        protected boolean isMakeUp(){
            return false;
        }

        /**穿衣服*/
        protected abstract void dressing();

        /**刷牙*/
        protected void brushTeeth(){
            System.out.println("刷牙");
        }

        /**洗脸*/
        protected void washFace(){
            System.out.println("洗脸");
        }

        /**吃早餐*/
        protected abstract void eatBreakFast();

        /**挂钩*/
        protected void hook(){};

    }

    女生具体实现类,重写isMakeUp()方法,内容如下:

    /**
     * 女生
     * 具体实现类
     */
    public class WomanPerson extends AbstractPerson{

        @Override
        protected void dressing() {
            System.out.println("穿休闲衣服");
        }

        @Override
        protected void eatBreakFast() {
            System.out.println("在家弄点吃的,或者在外面买一点小吃");
        }

        @Override
        protected boolean isMakeUp() {
            return true;
        }
    }

    运行测试类,女生具体实现类,输出结果:

    -----女生起床步骤----
    穿休闲衣服
    刷牙
    洗脸
    进行化妆
    在家弄点吃的,或者在外面买一点小吃

    三、应用

    模版设计模式,应用非常广泛,比如javaEE中的servlet,当我们每创建一个servlet的时候,都会继承HttpServlet,其实HttpServlet已经为我们提供一套操作流程,我们只需要重写里面的方法即可!

    代码复用神器,模板模式实操分享

     

    HttpServlet 的部分源码如下:

    public abstract class HttpServlet extends GenericServlet {
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // ...
        }
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // ...
        }
        protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // ...
        }
        protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // ...
        }
        protected void doDelete(HttpServletRequest req,  HttpServletResponse resp) throws ServletException, IOException {
            // ...
        }
        protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // ...
        }
        protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // ...
        }
        
        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 {
                //
                // Note that this means NO servlet supports whatever
                // method was requested, anywhere on this server.
                //

                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);
            }
        }
        
        // ...省略...
    }

    自定义一个 HelloWorld 的 Servlet 类,如下:

    import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;

    public class HelloWorld extends HttpServlet {

      public void init() throws ServletException {
        // ...
      }

      public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          response.setContentType("text/html");
          PrintWriter out = response.getWriter();
          out.println("<h1>Hello World!</h1>");
      }
      
      public void destroy() {
          // ...
      }
    }

    四、总结

    模版模式有着许多的优点:

    1、模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码;

    2、子类实现算法的某些细节,有助于算法的扩展;

    3、通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合开放-封闭原则;

    也有些缺点:每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象如果某些类有一些共同的行为,可以使用模版设计模式,创建一个抽象类,将共同的行为定义在抽象类中,可以有效的减少子类重复的代码。

  • 相关阅读:
    PostgreSQL远端访问
    PostgreSQL在线安装
    /usr/lib64改名字风波
    Provisional headers are shown(一)
    解析URL中的携带的参数到Map
    Mysql5.7的初始密码更改
    REST开放接口生成文档工具之apidoc
    自己来实现一套IOC注解框架
    RecyclerView打造通用的万能Adapter
    RecyclerView分隔线定制
  • 原文地址:https://www.cnblogs.com/jiagoushijuzi/p/13455021.html
Copyright © 2011-2022 走看看