• 万字分析几种常用设计模式的应用


    一、适配器模式

    1. 描述

    将一个类的接口适配成另一个类的接口所期望的形式,适配器模式实际上是一种补救模式,为了适应两种不同的标准而产生的一种兼容方案。

    在现实生活中,电源适配器就是一个非常容易帮助我们理解适配器模式的案例,比如有些国家标准电压是110V,我们国家则是220V,两个标准都不能修改,又不得不使用,于是就有了电源适配器,可以接入110V,输出220V,或者接入220V,输出110V。

    类似的场景还有很多,比如翻译、转接头等等。

    2. 标准结构

    在这里插入图片描述

    /**
     * 目标接口,即客户端本身所期望请求的接口
     */
    public interface Target {
    
        void request();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    /**
     * 适配者,需要适配的接口
     */
    public class Adaptee {
    
        public void specificRequest(){
            System.out.println("特殊的请求");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    /**
     * 适配器,转换的接口
     */
    public class Adapter implements Target {
    
        private Adaptee adaptee = new Adaptee();
    
        @Override
        public void request() {
            System.out.println("正常请求");
            adaptee.specificRequest();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    public class Test {
        public static void main(String[] args) {
            // 对客户端来说,只知道一个request接口
            Target target = new Adapter();
            target.request();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3. 使用场景

    1. 系统需要使用现有的类,而此类的接口不符合系统的需要

    2. 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。

    3. 通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)

    优点

    1. 可以让任何两个没有关联的类一起运行。

    2. 提高了类的复用

    3. 增加了类的透明度

    4. 灵活性好。

    缺点

    1. 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
    2. 由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。

    4. 使用案例

    4.1 spring-webmvc

    适配器,spring-webmvc定义的标准处理请求的方法为handle,这个方法中有一个handler参数,实际上就是适配者

    public interface HandlerAdapter {
    
    	boolean supports(Object handler);
    
    	@Nullable
    	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    
    	long getLastModified(HttpServletRequest request, Object handler);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    调用handle方法,实际上调用的是Servlet协议中定义的service方法,也就是我们适配的接口

    public class SimpleServletHandlerAdapter implements HandlerAdapter {
    
    	@Override
    	public boolean supports(Object handler) {
    		return (handler instanceof Servlet);
    	}
    
    	@Override
    	@Nullable
    	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
    			throws Exception {
    
    		((Servlet) handler).service(request, response);
    		return null;
    	}
    
    	@Override
    	public long getLastModified(HttpServletRequest request, Object handler) {
    		return -1;
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    HandlerAdapter接口的其他实现类,在handle方法中逻辑更为复杂,但无论怎样最终都会调用到目标接口:service

    4.2 MyBatis

    MyBatis中有一个Log接口,其定义了自己的日志级别,但同时为了能够适应其他日志框架的日志级别,于是又针对每一种日志框架做了不同的实现。

    MyBatis中自己的日志级别标准

    public interface Log {
    
      boolean isDebugEnabled();
    
      boolean isTraceEnabled();
    
      void error(String s, Throwable e);
    
      void error(String s);
    
      void debug(String s);
    
      void trace(String s);
    
      void warn(String s);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    适应别的日志框架

    public class Jdk14LoggingImpl implements Log {
    
      private final Logger log;
    
      public Jdk14LoggingImpl(String clazz) {
        log = Logger.getLogger(clazz);
      }
    
      @Override
      public boolean isDebugEnabled() {
        return log.isLoggable(Level.FINE);
      }
    
      @Override
      public boolean isTraceEnabled() {
        return log.isLoggable(Level.FINER);
      }
    
      @Override
      public void error(String s, Throwable e) {
        log.log(Level.SEVERE, s, e);
      }
    
      @Override
      public void error(String s) {
        log.log(Level.SEVERE, s);
      }
    
      @Override
      public void debug(String s) {
        log.log(Level.FINE, s);
      }
    
      @Override
      public void trace(String s) {
        log.log(Level.FINER, s);
      }
    
      @Override
      public void warn(String s) {
        log.log(Level.WARNING, s);
      }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    4.3 JDK

    Jdk中的WindowAdapter、MouseAdapter、KeyAdapter等等awt.event包下有很多类似这样的适配类,这是一种非典型的使用方法,他们分别实现了各自的XXXListener接口,但却没有对其中的逻辑进行重写,都是空现实,把具体实现逻辑交给了具体的调用者,这样做的目的主要是因为这些XXXListener接口中的方法较多,但调用者又不会每一个接口方法都会用到,所以就通过适配类来全部实现,而调用者只需要继承适配类,重写自己需要的方法即可。

    public abstract class WindowAdapter
        implements WindowListener, WindowStateListener, WindowFocusListener
    {
        /**
         * Invoked when a window has been opened.
         */
        public void windowOpened(WindowEvent e) {}
    
        /**
         * Invoked when a window is in the process of being closed.
         * The close operation can be overridden at this point.
         */
        public void windowClosing(WindowEvent e) {}
    
        /**
         * Invoked when a window has been closed.
         */
        public void windowClosed(WindowEvent e) {}
    
        /**
         * Invoked when a window is iconified.
         */
        public void windowIconified(WindowEvent e) {}
    
        /**
         * Invoked when a window is de-iconified.
         */
        public void windowDeiconified(WindowEvent e) {}
    
        /**
         * Invoked when a window is activated.
         */
        public void windowActivated(WindowEvent e) {}
    
        /**
         * Invoked when a window is de-activated.
         */
        public void windowDeactivated(WindowEvent e) {}
    
        /**
         * Invoked when a window state is changed.
         * @since 1.4
         */
        public void windowStateChanged(WindowEvent e) {}
    
        /**
         * Invoked when the Window is set to be the focused Window, which means
         * that the Window, or one of its subcomponents, will receive keyboard
         * events.
         *
         * @since 1.4
         */
        public void windowGainedFocus(WindowEvent e) {}
    
        /**
         * Invoked when the Window is no longer the focused Window, which means
         * that keyboard events will no longer be delivered to the Window or any of
         * its subcomponents.
         *
         * @since 1.4
         */
        public void windowLostFocus(WindowEvent e) {}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    public static void main(String[] args) {
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                super.windowClosing(e);
            }
        });
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4.4 Spring

    同样的方式,在Spring事务中也有运用到,也是为了实现者不必每次都要实现TransactionSynchronization接口的所有方法,不过从spring 5.3版本开始,由于JDK支持接口可以有默认的实现方法,因此接口实现者也就不需要实现全部接口了,自然TransactionSynchronizationAdapter也就没有存在的意义了。

    /**
     * Simple {@link TransactionSynchronization} adapter containing empty
     * method implementations, for easier overriding of single methods.
     *
     * 

    Also implements the {@link Ordered} interface to enable the execution * order of synchronizations to be controlled declaratively. The default * {@link #getOrder() order} is {@link Ordered#LOWEST_PRECEDENCE}, indicating * late execution; return a lower value for earlier execution. * * @author Juergen Hoeller * @since 22.01.2004 * @deprecated as of 5.3, in favor of the default methods on the * {@link TransactionSynchronization} interface */ @Deprecated public abstract class TransactionSynchronizationAdapter implements TransactionSynchronization, Ordered { @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } @Override public void suspend() { } @Override public void resume() { } @Override public void flush() { } @Override public void beforeCommit(boolean readOnly) { } @Override public void beforeCompletion() { } @Override public void afterCommit() { } @Override public void afterCompletion(int status) { } }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    二、责任链模式

    1. 描述

    责任链模式也是非常实用的一种设计模式,尤其是在许多框架开发中都有应用,因为它可以用来提供框架的扩展点,一种非常经典的应用场景类型就是过滤器、拦截器。

    责任链模式是一种行为型的设计模式,主要是为了将请求的发送和接收解耦,这样就可以方便扩展多个对象来处理请求,每个对象之间的处理顺序可以自由调配,每个对象处理后的数据也可以互相传递。

    2. 标准结构

    在这里插入图片描述

    public abstract class Handler {
    
        private Handler successor;
    
        public void setSuccessor(Handler successor) {
            this.successor = successor;
        }
    
        public Handler getSuccessor() {
            return successor;
        }
    
        public abstract void handleRequest(String msg);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    import org.apache.commons.lang3.StringUtils;
    
    public class ConcreteHandlerA extends Handler {
    
        @Override
        public void handleRequest(String msg) {
            if (StringUtils.isNotEmpty(msg)) {
                System.out.println("ConcreteHandlerA pass");
                if (getSuccessor() != null) {
                    getSuccessor().handleRequest(msg);
                }
            } else {
                System.out.println("msg 不能为空!");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    public class ConcreteHandlerB extends Handler {
    
        @Override
        public void handleRequest(String msg) {
            if (msg.length() <= 16) {
                System.out.println("ConcreteHandlerB pass");
                if (getSuccessor() != null) {
                    getSuccessor().handleRequest(msg);
                }
            } else {
                System.out.println("msg长度不能超过16!");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    public class Main {
        public static void main(String[] args) {
            Handler handler1 = new ConcreteHandlerA();
            Handler handler2 = new ConcreteHandlerB();
            handler1.setSuccessor(handler2);
            handler1.handleRequest("text");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3. 使用场景

    前面有介绍了,责任链模式主要就是为了避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

    因此责任链模式在针对这种需要多个对象配合处理的情况下非常好用,比如:多级别的审批流程、多级别的数据安全防护处理,还有框架中常见的过滤器、拦截器等。

    优点

    1. 降低耦合度,它将请求的发送者和接收者解耦。

    2. 简化了对象,使得对象不需要知道链的结构。

    3. 增强给对象指派职责的灵活性,通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。

    4. 增加新的请求处理类很方便。

    缺点

    1. 不能保证请求一定被接收。
    2. 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。
    3. 责任链容易配置错误,增加了代码的出错风险。

    4. 使用案例

    4.1 Servlet Filter

    Servlet规范中有一个非常经典的设计就是Filter,它主要用来实现对HTTP请求的过滤,比如:鉴权、限流、参数校验、记录日志等等。

    在这里插入图片描述

    Filter接口

    Filter就相当于Handler

    public interface Filter {
    
    	public void init(FilterConfig filterConfig) throws ServletException;
    	
        public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException;
    
    	public void destroy();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    FilterChain

    FilterChain相当于前面main方法中的逻辑,通常会做一层封装,封装后就是HandlerChain.

    public interface FilterChain {
    
        public void doFilter ( ServletRequest request, ServletResponse response ) throws IOException, ServletException;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    我们知道Servlet只定义标准,具体实现由各个web容器来完成,比如ApplicationFilterChain,就是tomcat中提供的实现类。

        @Override
        public void doFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException {
    
            // 省略部分代码...
            
            internalDoFilter(request,response);
            
        }
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
        private void internalDoFilter(ServletRequest request,
                                      ServletResponse response)
            throws IOException, ServletException {
    
            // Call the next filter if there is one
            if (pos < n) {
                ApplicationFilterConfig filterConfig = filters[pos++];
               
                Filter filter = filterConfig.getFilter();
    				
    			// 省略部分代码...
    			// 通过递归 + pos下标的方式,遍历处理所有的filter	
                filter.doFilter(request, response, this);
                    
                
                return;
            }
            
            // 省略部分代码...
            servlet.service(request, response);
            
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    给filterChain添加filter

    for (FilterMap filterMap : filterMaps) {
        if (!matchDispatcher(filterMap, dispatcher)) {
            continue;
        }
        if (!matchFiltersURL(filterMap, requestPath))
            continue;
        ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMap.getFilterName());
        if (filterConfig == null) {
            // FIXME - log configuration problem
            continue;
        }
        filterChain.addFilter(filterConfig);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    4.2 Spring Interceptor

    Spring Interceptor和Servlet Filter差不多,是spring框架中的功能,当客户端的请求发送过来后,会先经过Servlet Filter再经过Spring Interceptor,最后才到达业务代码中。

    HandlerInterceptor

    HandlerInterceptor接口相当于Handler

    public interface HandlerInterceptor {
    
    	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    			throws Exception {
    
    		return true;
    	}
    
    	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
    			@Nullable ModelAndView modelAndView) throws Exception {
    	}
    
    	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
    			@Nullable Exception ex) throws Exception {
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    HandlerExecutionChain

    HandlerExecutionChain接口相当于HandlerChain

    public class HandlerExecutionChain {
    
    	private final Object handler;
    	private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
    	private int interceptorIndex = -1;
    
    	public void addInterceptor(HandlerInterceptor interceptor) {
    		this.interceptorList.add(interceptor);
    	}
    	
    	boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    		for (int i = 0; i < this.interceptorList.size(); i++) {
    			HandlerInterceptor interceptor = this.interceptorList.get(i);
    			if (!interceptor.preHandle(request, response, this.handler)) {
    				triggerAfterCompletion(request, response, null);
    				return false;
    			}
    			this.interceptorIndex = i;
    		}
    		return true;
    	}
    
    	void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
    			throws Exception {
    		for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
    			HandlerInterceptor interceptor = this.interceptorList.get(i);
    			interceptor.postHandle(request, response, this.handler, mv);
    		}
    	}
    
    	void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
    		for (int i = this.interceptorIndex; i >= 0; i--) {
    			HandlerInterceptor interceptor = this.interceptorList.get(i);
    			try {
    				interceptor.afterCompletion(request, response, this.handler, ex);
    			}
    			catch (Throwable ex2) {
    				logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
    			}
    		}
    	}
    }
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    4.3 hutool Chain

    public static void main(String[] args) {
        CharSequence str = "

    hello world

    "
    ; String escape = EscapeUtil.escapeHtml4(str); System.out.println(escape); String unescape = EscapeUtil.unescapeHtml4(escape); System.out.println(unescape); }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    输出结果

    <html><p>hello world</p></html>
    

    hello world

    • 1
    • 2

    escapeHtml4方法可以转义html中的特殊字符,其中就使用到了责任链模式来完成特殊字符的替换。

    写明了就是要用责任链模式,设计了一个通用的接口。

    Chain

    /**
     * 责任链接口
     * @author Looly
     *
     * @param  元素类型
     * @param  目标类类型,用于返回this对象
     */
    public interface Chain<E, T> extends Iterable<E>{
    	/**
    	 * 加入责任链
    	 * @param element 责任链新的环节元素
    	 * @return this
    	 */
    	T addChain(E element);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    ReplacerChain

    /**
     * 字符串替换链,用于组合多个字符串替换逻辑
     * 
     * @author looly
     * @since 4.1.5
     */
    public class ReplacerChain extends StrReplacer implements Chain<StrReplacer, ReplacerChain> {
    	private static final long serialVersionUID = 1L;
    
    	private final List<StrReplacer> replacers = new LinkedList<>();
    
    	/**
    	 * 构造
    	 * 
    	 * @param strReplacers 字符串替换器
    	 */
    	public ReplacerChain(StrReplacer... strReplacers) {
    		for (StrReplacer strReplacer : strReplacers) {
    			addChain(strReplacer);
    		}
    	}
    
    	@SuppressWarnings("NullableProblems")
    	@Override
    	public Iterator<StrReplacer> iterator() {
    		return replacers.iterator();
    	}
    	
    	@Override
    	public ReplacerChain addChain(StrReplacer element) {
    		replacers.add(element);
    		return this;
    	}
    
    	@Override
    	protected int replace(CharSequence str, int pos, StrBuilder out) {
    		int consumed = 0;
    		for (StrReplacer strReplacer : replacers) {
    			consumed = strReplacer.replace(str, pos, out);
    			if (0 != consumed) {
    				return consumed;
    			}
    		}
    		return consumed;
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    添加chain的逻辑放在构造方法中

    public static String escapeHtml4(CharSequence html) {
    	Html4Escape escape = new Html4Escape();
    	return escape.replace(html).toString();
    }
    
    • 1
    • 2
    • 3
    • 4
    public Html4Escape() {
    	addChain(new LookupReplacer(BASIC_ESCAPE));
    	addChain(new LookupReplacer(ISO8859_1_ESCAPE));
    	addChain(new LookupReplacer(HTML40_EXTENDED_ESCAPE));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    责任链的写法有多种,可以使用一开始demo中的方式,省去HandlerChain,直接在每个Handler中设置后链的关系,也可以像FilterInterceptorHutool那样定义一个集合,把所有的Handler都放入集合中,并让HandlerChain去维护,调用的时候只需要遍历集合去处理即可。

    三、策略模式

    1. 描述

    策略模式是一种常用又特别容易理解的设计模式,它也是一种行为型模式,最常用于解决if...else...这样的分支逻辑判断,每一个if分支都对应一种不同的策略,或者叫算法,每一种算法独立存在,互不影响,互可替换,很明显策略模式起到的作用就是解耦、可扩展。

    2. 标准结构

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-it6yH5Gq-1658796551934)(C:\Users\wangyilun\AppData\Roaming\Typora\typora-user-images\1655726975579.png)]

    public interface Strategy {
    
        void algorithmInterface();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    public class ConcreteStrategyA implements Strategy {
        @Override
        public void algorithmInterface() {
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    public class ConcreteStrategyB implements Strategy {
        @Override
        public void algorithmInterface() {
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    public class ConcreteStrategyC implements Strategy {
        @Override
        public void algorithmInterface() {
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    public class Context {
    
        Strategy strategy;
    
        public Context(Strategy strategy){
            this.strategy = strategy;
        }
    
        public void contextInterface(){
            strategy.algorithmInterface();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    public class Test {
        public static void main(String[] args) {
            Context context = new Context(new ConcreteStrategyA());
            context.contextInterface();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3. 使用场景

    策略模式不仅仅可以用来解决if...else...问题,更重要的是可以解耦不同的业务处理逻辑,让代码更容易扩展,更容易维护,减少复杂度,满足开闭原则,所以一般而言只要某种业务存在多种算法或者行为,并且又需要方便切换,变更的场景,就可以使用策略模式。

    优点

    1. 解耦,容易扩展,满足开闭原则

    缺点

    1. 过多的策略模式容易造成类的数量太多
    2. 上层应用需要知道到底有哪些策略,以及应该使用哪一种,这不符合设计原则中的迪米特法则,不过完全可以结合其他方式来解决。

    4. 使用案例

    源码中使用策略模式的也非常多,这里不一一列举,但是我们有必要来看看一些没有完全按照标准结构来实现的策略模式。

    4.1 策略枚举

    public enum Strategy {
    
    		/**
    		 * Default: Retain the first value only.
    		 */
    		RETAIN_FIRST,
    
    		/**
    		 * Retain the last value only.
    		 */
    		RETAIN_LAST,
    
    		/**
    		 * Retain all unique values in the order of their first encounter.
    		 */
    		RETAIN_UNIQUE
    
    	}
    
    	void dedupe(HttpHeaders headers, Config config) {
    		String names = config.getName();
    		Strategy strategy = config.getStrategy();
    		if (headers == null || names == null || strategy == null) {
    			return;
    		}
    		for (String name : names.split(" ")) {
    			dedupe(headers, name.trim(), strategy);
    		}
    	}
    
    	private void dedupe(HttpHeaders headers, String name, Strategy strategy) {
    		List<String> values = headers.get(name);
    		if (values == null || values.size() <= 1) {
    			return;
    		}
    		switch (strategy) {
    		case RETAIN_FIRST:
    			headers.set(name, values.get(0));
    			break;
    		case RETAIN_LAST:
    			headers.set(name, values.get(values.size() - 1));
    			break;
    		case RETAIN_UNIQUE:
    			headers.put(name, values.stream().distinct().collect(Collectors.toList()));
    			break;
    		default:
    			break;
    		}
    	}
    
    	public static class Config extends AbstractGatewayFilterFactory.NameConfig {
    
    		private Strategy strategy = Strategy.RETAIN_FIRST;
    
    		public Strategy getStrategy() {
    			return strategy;
    		}
    
    		public Config setStrategy(Strategy strategy) {
    			this.strategy = strategy;
    			return this;
    		}
    
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    4.2 匿名策略类

      private interface Strategy {
        Iterator<String> iterator(Splitter splitter, CharSequence toSplit);
      }
    
    • 1
    • 2
    • 3
      public static Splitter on(final CharMatcher separatorMatcher) {
        checkNotNull(separatorMatcher);
    
        return new Splitter(
            new Strategy() {
              @Override
              public SplittingIterator iterator(Splitter splitter, final CharSequence toSplit) {
                return new SplittingIterator(splitter, toSplit) {
                  @Override
                  int separatorStart(int start) {
                    return separatorMatcher.indexIn(toSplit, start);
                  }
    
                  @Override
                  int separatorEnd(int separatorPosition) {
                    return separatorPosition + 1;
                  }
                };
              }
            });
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
      private static Splitter on(final CommonPattern separatorPattern) {
        checkArgument(
            !separatorPattern.matcher("").matches(),
            "The pattern may not match the empty string: %s",
            separatorPattern);
    
        return new Splitter(
            new Strategy() {
              @Override
              public SplittingIterator iterator(final Splitter splitter, CharSequence toSplit) {
                final CommonMatcher matcher = separatorPattern.matcher(toSplit);
                return new SplittingIterator(splitter, toSplit) {
                  @Override
                  public int separatorStart(int start) {
                    return matcher.find(start) ? matcher.start() : -1;
                  }
    
                  @Override
                  public int separatorEnd(int separatorPosition) {
                    return matcher.end();
                  }
                };
              }
            });
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
      public static Splitter fixedLength(final int length) {
        checkArgument(length > 0, "The length may not be less than 1");
    
        return new Splitter(
            new Strategy() {
              @Override
              public SplittingIterator iterator(final Splitter splitter, CharSequence toSplit) {
                return new SplittingIterator(splitter, toSplit) {
                  @Override
                  public int separatorStart(int start) {
                    int nextChunkStart = start + length;
                    return (nextChunkStart < toSplit.length() ? nextChunkStart : -1);
                  }
    
                  @Override
                  public int separatorEnd(int separatorPosition) {
                    return separatorPosition;
                  }
                };
              }
            });
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    4.3 策略+工厂

    常见的组合方式

    Spring AOP中的应用
    public interface AopProxy {
    	
    	Object getProxy();
    	
    	Object getProxy(@Nullable ClassLoader classLoader);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    不同的策略实现

    class CglibAopProxy implements AopProxy, Serializable {
    }
    
    • 1
    • 2
    final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
    }
    
    • 1
    • 2
    class ObjenesisCglibAopProxy extends CglibAopProxy {
    }
    
    • 1
    • 2

    策略工厂定义

    public interface AopProxyFactory {
    
    	AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    具体策略工厂实现

    public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    
    	private static final boolean IN_NATIVE_IMAGE = (System.getProperty("org.graalvm.nativeimage.imagecode") != null);
    
    	// 根据不同参数,选择不同的策略(动态代理方式)
    	@Override
    	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    		if (!IN_NATIVE_IMAGE &&
    				(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
    			Class<?> targetClass = config.getTargetClass();
    			if (targetClass == null) {
    				throw new AopConfigException("TargetSource cannot determine target class: " +
    						"Either an interface or a target is required for proxy creation.");
    			}
    			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
    				return new JdkDynamicAopProxy(config);
    			}
    			return new ObjenesisCglibAopProxy(config);
    		}
    		else {
    			return new JdkDynamicAopProxy(config);
    		}
    	}
    
    	private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
    		Class<?>[] ifcs = config.getProxiedInterfaces();
    		return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    直接使用switch来构造
    public class Context {
        
        public static Strategy getStrategy(String Type) {
            switch (Type) {
                case "A":
                    return new ConcreteStrategyA();
                case "B":
                    return new ConcreteStrategyB();
                case "C":
                    return new ConcreteStrategyC();
                default:
                    throw new IllegalArgumentException("Type error!");
            }
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    public class Main {
        public static void main(String[] args) {
            Strategy strategyA = Context.getStrategy("A");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    Spring Bean自动注入
    public interface Strategy {
    	// 增加一个策略枚举类
        StrategyEnum getStrategyType();
    
        void algorithmInterface();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    public enum StrategyEnum {
    
        StrategyA,
    
        StrategyB,
    
        StrategyC
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    @Component
    public class ConcreteStrategyA implements Strategy {
        @Override
        public StrategyEnum getStrategyType() {
            return StrategyEnum.StrategyA;
        }
    
        @Override
        public void algorithmInterface() {
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    @Component
    public class ConcreteStrategyB implements Strategy {
    
        @Override
        public StrategyEnum getStrategyType() {
            return StrategyEnum.StrategyB;
        }
    
        @Override
        public void algorithmInterface() {
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    @Component
    public class ConcreteStrategyC implements Strategy {
    
        @Override
        public StrategyEnum getStrategyType() {
            return StrategyEnum.StrategyC;
        }
    
        @Override
        public void algorithmInterface() {
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    @Component
    public class StrategyContext implements ApplicationContextAware {
    
        private static final Map<StrategyEnum, Strategy> registerMap = new ConcurrentHashMap<>();
    
        public void algorithmInterface(StrategyEnum strategyEnum) {
            registerMap.get(strategyEnum).algorithmInterface();
        }
    	
        // 自动注入到map中
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            Map<String, Strategy> strategyMap = applicationContext.getBeansOfType(Strategy.class);
            strategyMap.values().forEach(strategyService -> registerMap.put(strategyService.getStrategyType(), strategyService));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    使用方式

    @Resource
    private StrategyContext strategyContext;
    
    @Test
    public void contextRegister(){
        strategyContext.algorithmInterface(StrategyEnum.StrategyA);
        strategyContext.algorithmInterface(StrategyEnum.StrategyB);
        strategyContext.algorithmInterface(StrategyEnum.StrategyC);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    四、建造者模式

    1. 描述

    建造者模式属于创建型模式,它可以用来优雅的构建一个复杂的对象,其定义如下:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

    2. 标准结构

    在这里插入图片描述

    public class Product {
    
        private String id;
    
        private String name;
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    public interface Builder {
    
        void buildId();
    
        void buildName();
    
        Product build();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    public class ConcreteBuilder implements Builder {
    
        private Product product = new Product();
    
        @Override
        public void buildId() {
            product.setId("1");
        }
    
        @Override
        public void buildName() {
            product.setName("abc");
        }
    
        @Override
        public Product build() {
            return product;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    public class Director {
    
        public Product getProduct(Builder builder){
            builder.buildId();
            builder.buildName();
            return builder.build();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    public class Test {
        public static void main(String[] args) {
            Director director = new Director();
            Product product = director.getProduct(new ConcreteBuilder());
            System.out.println(product);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3. 使用场景

    当一个对象构建的属性特别多,又或者是构建起来步骤特别的多,内部结构复杂,但整体的组成逻辑通常是稳定的,其对象的组成部件基本不会改变,但组合方式或者部件的内部逻辑经常会发生改变,当遇到这类场景时,就可以考虑使用构造者模式了。

    现在建造者模式也常常用来简化普通对象的构建,比如lombok提供的@Builder注解。

    平时我们构建对象时一般都是像如下这样,在属性比较少,且不复杂时完全没有问题,但如果属性很多,就不太合适了。

    // 构造方法
    Product p = new Product("2","def");
    
    // set方法
    Product p = new Product();
    p.setId("2");
    p.setName("def");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    lombok提供的@Builder

    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public class Product {
    
        private String id;
    
        private String name;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    直接这样使用即可,看起来非常的简洁

    Product p = Product.builder()
    	.id("2")
    	.name("def")
    	.build();
    
    • 1
    • 2
    • 3
    • 4

    编辑后的class文件,可以看到具体的实现方式

    public class Product {
        private String id;
        private String name;
    
        public static Product.ProductBuilder builder() {
            return new Product.ProductBuilder();
        }
    
        public String getId() {
            return this.id;
        }
    
        public String getName() {
            return this.name;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public boolean equals(Object o) {
            if (o == this) {
                return true;
            } else if (!(o instanceof Product)) {
                return false;
            } else {
                Product other = (Product)o;
                if (!other.canEqual(this)) {
                    return false;
                } else {
                    Object this$id = this.getId();
                    Object other$id = other.getId();
                    if (this$id == null) {
                        if (other$id != null) {
                            return false;
                        }
                    } else if (!this$id.equals(other$id)) {
                        return false;
                    }
    
                    Object this$name = this.getName();
                    Object other$name = other.getName();
                    if (this$name == null) {
                        if (other$name != null) {
                            return false;
                        }
                    } else if (!this$name.equals(other$name)) {
                        return false;
                    }
    
                    return true;
                }
            }
        }
    
        protected boolean canEqual(Object other) {
            return other instanceof Product;
        }
    
        public int hashCode() {
            int PRIME = true;
            int result = 1;
            Object $id = this.getId();
            int result = result * 59 + ($id == null ? 43 : $id.hashCode());
            Object $name = this.getName();
            result = result * 59 + ($name == null ? 43 : $name.hashCode());
            return result;
        }
    
        public String toString() {
            return "Product(id=" + this.getId() + ", name=" + this.getName() + ")";
        }
    
        public Product() {
        }
    
        public Product(String id, String name) {
            this.id = id;
            this.name = name;
        }
    
        public static class ProductBuilder {
            private String id;
            private String name;
    
            ProductBuilder() {
            }
    
            public Product.ProductBuilder id(String id) {
                this.id = id;
                return this;
            }
    
            public Product.ProductBuilder name(String name) {
                this.name = name;
                return this;
            }
    
            public Product build() {
                return new Product(this.id, this.name);
            }
    
            public String toString() {
                return "Product.ProductBuilder(id=" + this.id + ", name=" + this.name + ")";
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111

    优点

    1. 构建与表示分离,使代码更容易扩展。
    2. lombok使用的方式让代码的可读性更强。
    3. 使对象构建更加灵活,且更容易掌控构建细节。

    缺点

    1. 标准的建造者模式结构过于臃肿

    4. 使用案例

    源码中大多数的建造者模式都没有按照标准的结构来,所以我们学习设计模式一定要学会变通,万万不可生搬硬套。

    4.1 JDK

    JDK中Calendar类,就可以通过构造者方法的方式构建

    其有一个内部对象Builder,提供了一系列的set方法,以及最后构建的build方法。

    在这里插入图片描述

    		public Calendar build() {
                if (locale == null) {
                    locale = Locale.getDefault();
                }
                if (zone == null) {
                    zone = TimeZone.getDefault();
                }
                Calendar cal;
                if (type == null) {
                    type = locale.getUnicodeLocaleType("ca");
                }
                if (type == null) {
                    if (locale.getCountry() == "TH"
                        && locale.getLanguage() == "th") {
                        type = "buddhist";
                    } else {
                        type = "gregory";
                    }
                }
                switch (type) {
                case "gregory":
                    cal = new GregorianCalendar(zone, locale, true);
                    break;
                case "iso8601":
                    GregorianCalendar gcal = new GregorianCalendar(zone, locale, true);
                    // make gcal a proleptic Gregorian
                    gcal.setGregorianChange(new Date(Long.MIN_VALUE));
                    // and week definition to be compatible with ISO 8601
                    setWeekDefinition(MONDAY, 4);
                    cal = gcal;
                    break;
                case "buddhist":
                    cal = new BuddhistCalendar(zone, locale);
                    cal.clear();
                    break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, locale, true);
                    break;
                default:
                    throw new IllegalArgumentException("unknown calendar type: " + type);
                }
                cal.setLenient(lenient);
                if (firstDayOfWeek != 0) {
                    cal.setFirstDayOfWeek(firstDayOfWeek);
                    cal.setMinimalDaysInFirstWeek(minimalDaysInFirstWeek);
                }
                if (isInstantSet()) {
                    cal.setTimeInMillis(instant);
                    cal.complete();
                    return cal;
                }
    
                if (fields != null) {
                    boolean weekDate = isSet(WEEK_YEAR)
                                           && fields[WEEK_YEAR] > fields[YEAR];
                    if (weekDate && !cal.isWeekDateSupported()) {
                        throw new IllegalArgumentException("week date is unsupported by " + type);
                    }
    
                    // Set the fields from the min stamp to the max stamp so that
                    // the fields resolution works in the Calendar.
                    for (int stamp = MINIMUM_USER_STAMP; stamp < nextStamp; stamp++) {
                        for (int index = 0; index <= maxFieldIndex; index++) {
                            if (fields[index] == stamp) {
                                cal.set(index, fields[NFIELDS + index]);
                                break;
                            }
                        }
                    }
    
                    if (weekDate) {
                        int weekOfYear = isSet(WEEK_OF_YEAR) ? fields[NFIELDS + WEEK_OF_YEAR] : 1;
                        int dayOfWeek = isSet(DAY_OF_WEEK)
                                        ? fields[NFIELDS + DAY_OF_WEEK] : cal.getFirstDayOfWeek();
                        cal.setWeekDate(fields[NFIELDS + WEEK_YEAR], weekOfYear, dayOfWeek);
                    }
                    cal.complete();
                }
    
                return cal;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    4.2 MyBatis

    MyBatis中用到Build的方式还是挺多的,CacheBuilder、 MappedStatement、SqlSessionFactoryBuilder 等。

    Cache

    public Cache useNewCache(Class<? extends Cache> typeClass,
          Class<? extends Cache> evictionClass,
          Long flushInterval,
          Integer size,
          boolean readWrite,
          boolean blocking,
          Properties props) {
        Cache cache = new CacheBuilder(currentNamespace)
            .implementation(valueOrDefault(typeClass, PerpetualCache.class))
            .addDecorator(valueOrDefault(evictionClass, LruCache.class))
            .clearInterval(flushInterval)
            .size(size)
            .readWrite(readWrite)
            .blocking(blocking)
            .properties(props)
            .build();
        configuration.addCache(cache);
        currentCache = cache;
        return cache;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述

    MappedStatement

    public MappedStatement addMappedStatement(
          String id,
          SqlSource sqlSource,
          StatementType statementType,
          SqlCommandType sqlCommandType,
          Integer fetchSize,
          Integer timeout,
          String parameterMap,
          Class<?> parameterType,
          String resultMap,
          Class<?> resultType,
          ResultSetType resultSetType,
          boolean flushCache,
          boolean useCache,
          boolean resultOrdered,
          KeyGenerator keyGenerator,
          String keyProperty,
          String keyColumn,
          String databaseId,
          LanguageDriver lang,
          String resultSets) {
    
        if (unresolvedCacheRef) {
          throw new IncompleteElementException("Cache-ref not yet resolved");
        }
    
        id = applyCurrentNamespace(id, false);
        boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    
        MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
            .resource(resource)
            .fetchSize(fetchSize)
            .timeout(timeout)
            .statementType(statementType)
            .keyGenerator(keyGenerator)
            .keyProperty(keyProperty)
            .keyColumn(keyColumn)
            .databaseId(databaseId)
            .lang(lang)
            .resultOrdered(resultOrdered)
            .resultSets(resultSets)
            .resultMaps(getStatementResultMaps(resultMap, resultType, id))
            .resultSetType(resultSetType)
            .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
            .useCache(valueOrDefault(useCache, isSelect))
            .cache(currentCache);
    
        ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
        if (statementParameterMap != null) {
          statementBuilder.parameterMap(statementParameterMap);
        }
    
        MappedStatement statement = statementBuilder.build();
        configuration.addMappedStatement(statement);
        return statement;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    在这里插入图片描述

    4.3 Guava

    无独有偶,Guava中的缓存对象,也使用了Builder的方式

    在这里插入图片描述

    4.4 Spring MVC

    SpringMVC中构建ServerHttpRequest对象的方式,到还挺符合建造者模式的标准结构的

    ServerHttpRequest接口内部还有一个Builder接口

    在这里插入图片描述

    注意这个默认方法

    default ServerHttpRequest.Builder mutate() {
        return new DefaultServerHttpRequestBuilder(this);
    }
    
    • 1
    • 2
    • 3
    interface Builder {
    
    		Builder uri(URI uri);
    
    		Builder path(String path);
    
    		Builder contextPath(String contextPath);
    
    		Builder header(String headerName, String... headerValues);
    
    		Builder headers(Consumer<HttpHeaders> headersConsumer);
    
    		Builder sslInfo(SslInfo sslInfo);
    
    		Builder remoteAddress(InetSocketAddress remoteAddress);
    
    		ServerHttpRequest build();
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    然后有具体的实现类

    class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder {
    
    	private URI uri;
    
    	private HttpHeaders headers;
    
    	private String httpMethodValue;
    
    	@Nullable
    	private String uriPath;
    
    	@Nullable
    	private String contextPath;
    
    	@Nullable
    	private SslInfo sslInfo;
    
    	@Nullable
    	private InetSocketAddress remoteAddress;
    
    	private Flux<DataBuffer> body;
    
    	private final ServerHttpRequest originalRequest;
    
    
    	public DefaultServerHttpRequestBuilder(ServerHttpRequest original) {
    		Assert.notNull(original, "ServerHttpRequest is required");
    
    		this.uri = original.getURI();
    		this.headers = HttpHeaders.writableHttpHeaders(original.getHeaders());
    		this.httpMethodValue = original.getMethodValue();
    		this.contextPath = original.getPath().contextPath().value();
    		this.remoteAddress = original.getRemoteAddress();
    		this.body = original.getBody();
    		this.originalRequest = original;
    	}
    
    
    	@Override
    	public ServerHttpRequest.Builder method(HttpMethod httpMethod) {
    		this.httpMethodValue = httpMethod.name();
    		return this;
    	}
    
    	@Override
    	public ServerHttpRequest.Builder uri(URI uri) {
    		this.uri = uri;
    		return this;
    	}
    
    	@Override
    	public ServerHttpRequest.Builder path(String path) {
    		Assert.isTrue(path.startsWith("/"), "The path does not have a leading slash.");
    		this.uriPath = path;
    		return this;
    	}
    
    	@Override
    	public ServerHttpRequest.Builder contextPath(String contextPath) {
    		this.contextPath = contextPath;
    		return this;
    	}
    
    	@Override
    	public ServerHttpRequest.Builder header(String headerName, String... headerValues) {
    		this.headers.put(headerName, Arrays.asList(headerValues));
    		return this;
    	}
    
    	@Override
    	public ServerHttpRequest.Builder headers(Consumer<HttpHeaders> headersConsumer) {
    		Assert.notNull(headersConsumer, "'headersConsumer' must not be null");
    		headersConsumer.accept(this.headers);
    		return this;
    	}
    
    	@Override
    	public ServerHttpRequest.Builder sslInfo(SslInfo sslInfo) {
    		this.sslInfo = sslInfo;
    		return this;
    	}
    
    	@Override
    	public ServerHttpRequest.Builder remoteAddress(InetSocketAddress remoteAddress) {
    		this.remoteAddress = remoteAddress;
    		return this;
    	}
    
    	@Override
    	public ServerHttpRequest build() {
    		return new MutatedServerHttpRequest(getUriToUse(), this.contextPath,
    				this.httpMethodValue, this.sslInfo, this.remoteAddress, this.body, this.originalRequest);
    	}
        
        // ...省略部分代码
    }	
    	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97

    具体用法,先调用mutate方法,获得默认的构造者实现类,即:DefaultServerHttpRequestBuilder,然后再根据需要选择是否重写方法。

    比如AddRequestHeaderGatewayFilterFactory重写了header方法

    ServerHttpRequest request = exchange.getRequest().mutate().header(config.getName(), value).build();
    
    • 1

    ForwardPathFilter重写了path方法

    exchange = exchange.mutate().request(exchange.getRequest().mutate().path(routeUri.getPath()).build()).build();
    
    • 1

    五、工厂模式

    1. 描述

    工厂模式应该是我们最常遇见的设计模式了,我们见过太多XXXFactory这样的类名了,同样,它也是创建型模式,工厂模式又分为简单工厂模式、工厂模式、抽象工厂模式,其中前两种是非常常见、实用的方式,相比抽象工厂模式比较复杂,实际中使用的也非常少,所以不过多介绍。

    2. 标准结构

    简单工厂模式

    在这里插入图片描述

    工厂模式

    在这里插入图片描述

    public interface OrderFactory {
        Order createOrder();
    }
    
    • 1
    • 2
    • 3
    public class NormalOrderFactory implements OrderFactory {
        @Override
        public Order createOrder() {
            return new NormalOrder();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    public class VIPOrderFactory implements  OrderFactory {
        @Override
        public Order createOrder() {
            return new VIPOrder();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    public interface Order {
        void create();
    }
    
    • 1
    • 2
    • 3
    public class NormalOrder implements Order {
        @Override
        public void create() {
            System.out.println("普通的订单");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    public class VIPOrder implements Order {
        @Override
        public void create() {
            System.out.println("VIP的订单");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    简单工厂模式

    public class SimpleOrderFactory {
    
        private static final Map<String, Order> orderMap = new HashMap<>();
    
        static {
            orderMap.put("normal", new NormalOrder());
            orderMap.put("vip", new VIPOrder());
        }
    
        public static Order createOrder(String orderType) {
            return orderMap.get(orderType);
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    public class Test {
        public static void main(String[] args) {
        	
        	// 使用工厂模式
            OrderFactory normalOrderFactory = new NormalOrderFactory();
            Order normalOrder = normalOrderFactory.createOrder();
            normalOrder.create();
    
            OrderFactory vipOrderFactory = new VIPOrderFactory();
            Order vipOrder = vipOrderFactory.createOrder();
            vipOrder.create();
            
            // 使用简单工厂模式
            Order vip = SimpleOrderFactory.createOrder("vip");
            vip.create();
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3. 使用场景

    工厂模式使用起来非常简单,只要稍微复杂一点的对象,都可以使用工厂模式来创建,以此来简化客户端的逻辑。

    优点

    扩展性好、避免大量的if...else...代码,将复杂的逻辑封装起来,客户端直接调用即可,对客户端友好。

    缺点

    每增加一个新的产品时,就需要实现一套具体的类和工厂,这样会使得相关业务中的类数量非常多,一定程度上也增加了系统的复杂度。

    4. 使用案例

    工厂模式的应用真的非常多,最典型的应用应属Spring IOC容器,其中BeanFactory是其核心的底层接口。

    此外,工厂模式还有很多变种,有一些使用了工厂模式的思想,但并没有完全按照标准的结构来实现,这也是能运用好设计模式的关键。

    比如JDK中的Calendar类中的getInstance方法,根据传入不同的参数,创建出不同的Calendar对象

    public static Calendar getInstance()
        {
            return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
        }
    
    
        public static Calendar getInstance(TimeZone zone)
        {
            return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
        }
    
       
        public static Calendar getInstance(Locale aLocale)
        {
            return createCalendar(TimeZone.getDefault(), aLocale);
        }
    
        
        public static Calendar getInstance(TimeZone zone,
                                           Locale aLocale)
        {
            return createCalendar(zone, aLocale);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    private static Calendar createCalendar(TimeZone zone,
                                               Locale aLocale)
        {
            CalendarProvider provider =
                LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                     .getCalendarProvider();
            if (provider != null) {
                try {
                    return provider.getInstance(zone, aLocale);
                } catch (IllegalArgumentException iae) {
                    // fall back to the default instantiation
                }
            }
    
            Calendar cal = null;
    
            if (aLocale.hasExtensions()) {
                String caltype = aLocale.getUnicodeLocaleType("ca");
                if (caltype != null) {
                    switch (caltype) {
                    case "buddhist":
                    cal = new BuddhistCalendar(zone, aLocale);
                        break;
                    case "japanese":
                        cal = new JapaneseImperialCalendar(zone, aLocale);
                        break;
                    case "gregory":
                        cal = new GregorianCalendar(zone, aLocale);
                        break;
                    }
                }
            }
            if (cal == null) {
                // If no known calendar type is explicitly specified,
                // perform the traditional way to create a Calendar:
                // create a BuddhistCalendar for th_TH locale,
                // a JapaneseImperialCalendar for ja_JP_JP locale, or
                // a GregorianCalendar for any other locales.
                // NOTE: The language, country and variant strings are interned.
                if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                    cal = new BuddhistCalendar(zone, aLocale);
                } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                           && aLocale.getCountry() == "JP") {
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                } else {
                    cal = new GregorianCalendar(zone, aLocale);
                }
            }
            return cal;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    六、模板方法模式

    1. 描述

    模板方法模式一般用于有标准流程性的业务场景,总体流程不变,但流程中的每一个环节可能有所不同,用标准定义来描述就是,模板方法模式定义了一个算法的步骤,并允许子类为一个或多个步骤提供具体的实现,让子类可以在不改变算法架构的情况下,重新定义算法中的某些步骤。

    2. 标准结构

    在这里插入图片描述

    public abstract class AbstractClass {
    
        public final void templateMethod(){
            method1();
            method2();
            method3();
        }
    
        protected abstract void method3();
    
        protected final void method2(){
            System.out.println("AbstractClass method2");
        }
    
        protected abstract void method1();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    public class ConcreteClassA extends AbstractClass {
        @Override
        protected void method3() {
            System.out.println("ConcreteClassA method3");
        }
    
        @Override
        protected void method1() {
            System.out.println("ConcreteClassA method1");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    public class ConcreteClassB extends AbstractClass {
        @Override
        protected void method3() {
            System.out.println("ConcreteClassB method3");
        }
    
        @Override
        protected void method1() {
            System.out.println("ConcreteClassB method1");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3. 使用场景

    当业务逻存在一些需要多个步骤来完成,某些步骤是标准统一的,某些步骤根据不同的场景又有不同的处理逻辑,如果全部写在一个类中,可能那些不同场景步骤会出现各种条件判断,无论哪个场景发生修改,都会影响整个流程,这不符合开闭原则,但如果为每个场景都分别实现一套,同样对应相同的步骤就会出现重复的逻辑,又会带来大量的重复代码。

    那么,模板方法就是解决上述问题的一种非常好的模式,利用模板方法模式可以实现代码的复用和扩展,这实际上在对代码重构时就非常有帮助。

    优点

    对于不变的部分,与变的部分做到了隔离,符合开闭原则,使用业务非常容易扩展。

    对于公用逻辑易于维护。

    缺点

    由于每一个不同的实现都要一个子类来实现,会导致类的个数变得更多。

    4. 使用案例

    4.1 Spring

    AbstractApplicationContext类的refresh方法,这里就用到了模板方法模式,有一些子方法是由AbstractApplicationContext自己实现的,有一些子方法则是让不同的实现类各自实现。

    public void refresh() throws BeansException, IllegalStateException {
    		synchronized (this.startupShutdownMonitor) {
    			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
    
    			// Prepare this context for refreshing.
    			prepareRefresh();
    
    			// Tell the subclass to refresh the internal bean factory.
    			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
    			// Prepare the bean factory for use in this context.
    			prepareBeanFactory(beanFactory);
    
    			try {
    				// Allows post-processing of the bean factory in context subclasses.
    				postProcessBeanFactory(beanFactory);
    
    				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
    				// Invoke factory processors registered as beans in the context.
    				invokeBeanFactoryPostProcessors(beanFactory);
    
    				// Register bean processors that intercept bean creation.
    				registerBeanPostProcessors(beanFactory);
    				beanPostProcess.end();
    
    				// Initialize message source for this context.
    				initMessageSource();
    
    				// Initialize event multicaster for this context.
    				initApplicationEventMulticaster();
    
    				// Initialize other special beans in specific context subclasses.
    				onRefresh();
    
    				// Check for listener beans and register them.
    				registerListeners();
    
    				// Instantiate all remaining (non-lazy-init) singletons.
    				finishBeanFactoryInitialization(beanFactory);
    
    				// Last step: publish corresponding event.
    				finishRefresh();
    			}
    
    			catch (BeansException ex) {
    				if (logger.isWarnEnabled()) {
    					logger.warn("Exception encountered during context initialization - " +
    							"cancelling refresh attempt: " + ex);
    				}
    
    				// Destroy already created singletons to avoid dangling resources.
    				destroyBeans();
    
    				// Reset 'active' flag.
    				cancelRefresh(ex);
    
    				// Propagate exception to caller.
    				throw ex;
    			}
    
    			finally {
    				// Reset common introspection caches in Spring's core, since we
    				// might not ever need metadata for singleton beans anymore...
    				resetCommonCaches();
    				contextRefresh.end();
    			}
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    还有BeanPostProcessor类中定义的两个方法postProcessBeforeInitialization和postProcessAfterInitialization,我们一般会称之为前置处理器和后置处理器,光从名字就能感觉到有模板方法含义,实际上也是spring在某处留好了这两个方法的调用,所以应该说是模板方法中的钩子方法。

    4.2 Spring MVC

    AbstractController提供了handleRequest方法,负责请求处理并返回ModelAndView,其也是通过模板方法模式来实现,这点作者甚至直接写在了文档说明中。

    在这里插入图片描述

    public abstract class AbstractController extends WebContentGenerator implements Controller {
    
    	@Override
    	@Nullable
    	public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    		if (HttpMethod.OPTIONS.matches(request.getMethod())) {
    			response.setHeader("Allow", getAllowHeader());
    			return null;
    		}
    
    		// Delegate to WebContentGenerator for checking and preparing.
    		checkRequest(request);
    		prepareResponse(response);
    
    		// Execute handleRequestInternal in synchronized block if required.
    		if (this.synchronizeOnSession) {
    			HttpSession session = request.getSession(false);
    			if (session != null) {
    				Object mutex = WebUtils.getSessionMutex(session);
    				synchronized (mutex) {
    					return handleRequestInternal(request, response);
    				}
    			}
    		}
    
    		return handleRequestInternal(request, response);
    	}
    
    	/**
    	 * Template method. Subclasses must implement this.
    	 * The contract is the same as for {@code handleRequest}.
    	 * @see #handleRequest
    	 */
    	@Nullable
    	protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
    			throws Exception;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    4.3 JDK

    InputStream类

    public abstract class InputStream implements Closeable {
        
        // 留给子类实现
    	public abstract int read() throws IOException;
        
        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;
    	}
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    其他IO、NIO类库中也有很多类似的设计。

    HttpServlet类

    这也是比较经典的应用,其定义的doPost/doGet等方法是可以让子类进行重写的

    在这里插入图片描述

    public abstract class HttpServlet extends GenericServlet
        implements java.io.Serializable
    {
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
        {
            String protocol = req.getProtocol();
            String msg = lStrings.getString("http.method_get_not_supported");
            if (protocol.endsWith("1.1")) {
                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
            } else {
                resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
            }
        }
    
    
        protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
        {
            String protocol = req.getProtocol();
            String msg = lStrings.getString("http.method_post_not_supported");
            if (protocol.endsWith("1.1")) {
                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
            } else {
                resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
            }
        }
        
        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 / 1000 * 1000)) {
                    // 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);
            }
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    4.4 MyBatis

    BaseExecutor是MyBatis中经典的模板方法模式应用,其主要是用来执行SQL。

    query方法可以看作是定义的主流程,doQuery方法是其留给子类实现的。

    public abstract class BaseExecutor implements Executor {
        
      // 几个do开头的方法都是留给子类实现的
      protected abstract int doUpdate(MappedStatement ms, Object parameter)
          throws SQLException;
    
      protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
          throws SQLException;
    
      protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
          throws SQLException;
    
      protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
          throws SQLException;  
        
        
      @Override
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        BoundSql boundSql = ms.getBoundSql(parameter);
        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
        return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
     }
    
      @SuppressWarnings("unchecked")
      @Override
      public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
        if (closed) {
          throw new ExecutorException("Executor was closed.");
        }
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
          clearLocalCache();
        }
        List<E> list;
        try {
          queryStack++;
          list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
          if (list != null) {
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
          } else {
            list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
          }
        } finally {
          queryStack--;
        }
        if (queryStack == 0) {
          for (DeferredLoad deferredLoad : deferredLoads) {
            deferredLoad.load();
          }
          // issue #601
          deferredLoads.clear();
          if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
            // issue #482
            clearLocalCache();
          }
        }
        return list;
      }
    }
    
    
      private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        List<E> list;
        localCache.putObject(key, EXECUTION_PLACEHOLDER);
        try {
          // 具体query方式,交由子类实现
          list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
          localCache.removeObject(key);
        }
        localCache.putObject(key, list);
        if (ms.getStatementType() == StatementType.CALLABLE) {
          localOutputParameterCache.putObject(key, parameter);
        }
        return list;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
  • 相关阅读:
    [附源码]java毕业设计新航校园招聘管理系统论文2022
    TypeScrip6
    电子签名-为你的数据签字画押
    美容院如何用店内活动,开拓客源
    python爬虫连接redis数据库
    Python之路—200行Python代码搞了个打飞机游戏!!
    渗透测试之信息搜集
    [C/C++]数据结构 栈和队列()
    美团二面:为什么 Redis 会有哨兵?
    Java学习笔记——并发编程(三)
  • 原文地址:https://blog.csdn.net/CSDN_WYL2016/article/details/125987181