设计模式之美学习-行为型-模板模式(二十七)

什么是模板模式

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

模板模式的作用

复用以及扩展

标准实现

public abstract class AbstractClass {
    public final void templateMethod() {
        ....处理一些初始化操作 或者公用逻辑
        //...
        method1();
        //...
        method2();
        //...
    }
    //定义好的模板方法1
    protected abstract void method1();
    //定义好的模板方法2
    protected abstract void method2();
}

//实现1
public class ConcreteClass1 extends AbstractClass {
    //父类初始化了 专注自己的处理逻辑
    @Override
    protected void method1() {
        //...
    }

    @Override
    protected void method2() {
        //...
    }
}
//实现2
public class ConcreteClass2 extends AbstractClass {
    //父类初始化了 专注自己的处理逻辑
    @Override
    protected void method1() {
        //...
    }

    @Override
    protected void method2() {
        //...
    }
}

AbstractClass demo = ConcreteClass1();
demo.templateMethod();

 

源码中的使用

DispatcherServlet

org.springframework.web.servlet.FrameworkServlet做一些初始化操作

protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
        /**
         * <31>获取当前线程 国际化上下文 从里面会从2个ThreadLocal获取
         * org.springframework.web.filter.RequestContextFilter#initContextHolders
         * 如果配置了这个filter这里每次请求已经初始化 spring boot 验证
         */
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        //构建当前线程当本次请求的国际化上下文
        LocaleContext localeContext = this.buildLocaleContext(request);
        /**
         * <32>获取当前线程请求的RequestAttribute 里面会从2个ThreadLocal获取
         *  org.springframework.web.filter.RequestContextFilter#initContextHolders已经初始化 spring boot验证
         */
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        //<4>如果线程缓存没有构建过,表示之前没初始化过 
        ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
        /**
         *会从requsetAttribute里面获取获取不到初始化一个再set进去 key=WebAsyncUtils.WEB_ASYNC_MANAGER_ATTRIBUTE
         * 表示我们后去都可以根据reqeust.getAttribute(WebAsyncUtils.WEB_ASYNC_MANAGER_ATTRIBUTE)获取
         * 用于给管理异步请求
         */
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor());
        /**
         * <5>这里面会存入根据当前对象成语变量区分放到哪个线程缓存里面threadContextInheritable 默认为false
         * 我们可以利用前面的BeanWapper进行修改
         * 表示我们后续都可以根据LocaleContextHolder和RequestContextHolder获取
         * 
         */
        this.initContextHolders(request, localeContext, requestAttributes);

        try {
            //<6>抽象方法 由子类实现 如:DispatcherServlet实现
            this.doService(request, response);
        } catch (ServletException var17) {
            failureCause = var17;
            throw var17;
        } catch (IOException var18) {
            failureCause = var18;
            throw var18;
        } catch (Throwable var19) {
            failureCause = var19;
            throw new NestedServletException("Request processing failed", var19);
        } finally {
            /**
             *  <7>默认previousLocaleContext previousAttributes 是null
             *  这里主要是为了将线程缓存的previousLocaleContext previousAttributes清空
             */
            this.resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }

            if (this.logger.isDebugEnabled()) {
                if (failureCause != null) {
                    this.logger.debug("Could not complete request", (Throwable)failureCause);
                } else if (asyncManager.isConcurrentHandlingStarted()) {
                    this.logger.debug("Leaving response open for concurrent processing");
                } else {
                    this.logger.debug("Successfully completed request");
                }
            }
            //spring事件机制 发布ServletRequestHandlerEvent消息,这个请求是否执行成功都会发布消息的
            this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
        }

    }
//抽象方法
protected abstract void doService(HttpServletRequest var1, HttpServletResponse var2) throws Exception;

org.springframework.web.servlet.DispatcherServlet#doService

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (this.logger.isDebugEnabled()) {
            String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
            this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
        }
        //如果是include请求,保存request attribute快照数据,并在finally中进行还原
        Map<String, Object> attributesSnapshot = null;
        /**
         * request.getAttribute("javax.servlet.include.request_uri")!=null
         * <jsp:incluede page="xxx.jsp"/>
         * jsp里面嵌套上面这个标签 编译器后也是一个servlet来调用 通过这个属性判断是否是include标签
         */
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap();
            Enumeration attrNames = request.getAttributeNames();

            label108:
            while(true) {
                String attrName;
                do {
                    if (!attrNames.hasMoreElements()) {
                        break label108;
                    }

                    attrName = (String)attrNames.nextElement();
                } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));

                attributesSnapshot.put(attrName, request.getAttribute(attrName));
            }
        }
        //保存ApplicationContext 到request
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
        //保存localeResolver 到request
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        //保存themeResolver 到request
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        //保存gThemeSource 到request
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
        //貌似是为了解决重定向302 带参数 长度问题 先保存到服务器 然后302重定向只需要带过来一个对应的key
        FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
        if (inputFlashMap != null) {
            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
        }

        //保存一个空的FlashMap
        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
        //保存flashMapManager
        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

        try {
            //<7>执行处理请求
            this.doDispatch(request, response);
        } finally {
            //还原快照数据
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
                this.restoreAttributesAfterInclude(request, attributesSnapshot);
            }

        }

    }

摘自:《SpringMVC源码阅读-一个请求主要处理流程DispatcherServlet(四)》

模板模式和回调

回调标准实现

//回调接口实现
public interface ICallback {
    void methodToCallback();
}

public class BClass {
    public void process(ICallback callback) {
        ...执行一系列逻辑
        //...调用回调
        callback.methodToCallback();
        //...
    }
}

public class AClass {
    public static void main(String[] args) {
        BClass b = new BClass();
        //调用处理方法 传入回调
        b.process(new ICallback() { //回调对象
            @Override
            public void methodToCallback() {
                System.out.println("Call back me.");
            }
        });
    }
}

JdbcTemplate

传统通过jdbc实现访问数据库 我们可以发现大量的模板代码 如果 1  2 4  5  6 7 这些步骤几乎每次访问都需要

public class JdbcDemo {
    public User queryUser(long id) {
        Connection conn = null;
        Statement stmt = null;
        try {
            //1.加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo", "xzg", "xzg");

            //2.创建statement类对象,用来执行SQL语句
            stmt = conn.createStatement();

            //3.ResultSet类,用来存放获取的结果集
            String sql = "select * from user where id=" + id;
            ResultSet resultSet = stmt.executeQuery(sql);

            String eid = null, ename = null, price = null;

            //4.封装结果
            while (resultSet.next()) {
                User user = new User();
                user.setId(resultSet.getLong("id"));
                user.setName(resultSet.getString("name"));
                user.setTelephone(resultSet.getString("telephone"));
                return user;
            }
        } catch (ClassNotFoundException e) {
            // 5.TODO: log... 异常处理
        } catch (SQLException e) {
            // 6.TODO: log... 异常处理
        } finally {
            //7.释放连接
            if (conn != null)
                try {
                    conn.close();
                } catch (SQLException e) {
                    // TODO: log...
                }
            if (stmt != null)
                try {
                    stmt.close();
                } catch (SQLException e) {
                    // TODO: log...
                }
        }
        return null;
    }

}

jdbctemlate使用

public class JdbcTemplateDemo {
    private JdbcTemplate jdbcTemplate;

    public User queryUser(long id) {
        String sql = "select * from user where id="+id;
        //通过回调传入Mapper 我们只需要关心 封装结果集 和执行sql
        return jdbcTemplate.query(sql, new UserRowMapper()).get(0);
    }

    class UserRowMapper implements RowMapper<User> {
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            User user = new User();
            user.setId(rs.getLong("id"));
            user.setName(rs.getString("name"));
            user.setTelephone(rs.getString("telephone"));
            return user;
        }
    }
}

setClickListener

set存入成员变量

    Button button = (Button) findViewById(R.id.button);
    //注册点击事件
     button.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick (View v){
            System.out.println("I am clicked.");
        }
    });

2者区别

模板模式:通过再父类定义好算法骨架,将具体实现下放到子类 实现复用和扩展 本质通过继承

回调:定义好骨架 通过处理函数传入定制化的处理逻辑执行 或者提前拿通过成员变量注入,在骨架特定地点调用 实现不同需求的定制化逻辑,实现复用,本质是通过 实时传入回调或者组合的方式提前注入 实现代码复用

 

posted @ 2020-04-10 10:49  意犹未尽  阅读(174)  评论(0编辑  收藏  举报