zoukankan      html  css  js  c++  java
  • 设计模式之美学习-行为型-模板模式(二十七)

    什么是模板模式

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

    模板模式的作用

    复用以及扩展

    标准实现

    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者区别

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

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

  • 相关阅读:
    2018福大软工实践第六次作业
    2018福大软工实践第五次作业
    2018福大软工实践第四次作业
    2018福大软工实践第三次作业
    2018福大软工实践第二次作业
    团队现场编程实战(抽奖系统)
    团队Alpha版本冲刺(三)
    团队Alpha版本冲刺(二)
    团队Alpha版本冲刺(一)
    福大软工 · 第八次作业
  • 原文地址:https://www.cnblogs.com/LQBlog/p/12671929.html
Copyright © 2011-2022 走看看