• 设计模式① :适应设计模式


    一、前言

    有时候不想动脑子,就懒得看源码又不像浪费时间所以会看看书,但是又记不住,所以决定开始写"抄书"系列。本系列大部分内容都是来源于《 图解设计模式》(【日】结城浩 著),内容仅用于个人学习记录,可随意转载。

    二、Iterator 模式

    Iterator 模式 :一个一个遍历

    1. 介绍

    迭代器模式最常见的使用是使用Java 集合类时, 准确的说是 java.util.Collection 的实现类都具备了迭代器的类型,其实现也非常简单。

    迭代器模式中登场的角色有:

    • Iterator(迭代器):该角色负责定义而按顺序逐个遍历元素的接口。以 Java 的实现为例,其接口为 java.util.Iterator
    • ConcreteIterator (具体的迭代器):该角色负责实现 Iterator 角色所定义的接口。如 java.util.ArrayList的内部迭代器java.util.ArrayList.Itr
    • Aggregate(集合):该角色负责定义创建 Iterator 角色的接口。如 java.lang.Iterable
    • ConcreteAggregate(具体的集合) :该角色负责实现 Aggregate角色所定义的接口。如 java.util.ArrayList 。

    类图如下:
    在这里插入图片描述


    以上述内容举个例子如下:

    // 集合
    public interface Aggregate<T> {
        /**
         * 获取迭代器
         * @return
         */
        Iterator<T> iterator();
    }
    
    // 迭代器
    public interface Iterator<T> {
        /**
         * 是否存在下一个元素
         * @return
         */
        boolean hasNext();
    
        /**
         * 获取下一个元素
         * @return
         */
        T next();
    }
    
    // 具体的集合
    public class ConcreteAggregate implements Aggregate<String> {
    
        private final String[] designDemos = new String[10];
    
        public ConcreteAggregate() {
            // 初始化数据集
            for (int i = 0; i < designDemos.length; i++) {
                designDemos[i] = String.valueOf(i);
            }
        }
    
        public int getLength() {
            return designDemos.length;
        }
    
        public String get(int index){
            // TODO : 边界校验
            return designDemos[index];
        }
    
        @Override
        public Iterator<String> iterator() {
            return new ConcreteIterator(this);
        }
    }
    
    // 具体的迭代器
    public class ConcreteIterator implements Iterator<String> {
    
        private ConcreteAggregate concreteAggregate;
        private int index = 0;
    
        // TODO : 入参改造为面向接口
        public ConcreteIterator(ConcreteAggregate concreteAggregate) {
            this.concreteAggregate = concreteAggregate;
        }
    
        @Override
        public boolean hasNext() {
            return index < concreteAggregate.getLength();
        }
    
        @Override
        public String next() {
            // TODO : 一些范围校验
            final String info = concreteAggregate.get(index);
            index += 1;
            return info;
        }
    }
    
    // 测试类如下:
    public class IteratorDemoMain {
        public static void main(String[] args) {
            final Aggregate demoAggregate = new ConcreteAggregate();
    
            final Iterator<String> iterator = demoAggregate.iterator();
    
            while (iterator.hasNext()){
                final String next = iterator.next();
                System.out.println("next = " + next);
            }
        }
    }
    
    • 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

    2. 应用

    迭代器模式的典型应用就是 Java 中的 Collection 以及其子类,这个由于太熟悉就不再这里赘述。
    而在日常使用中,个人也没有自定义过迭代器模式使用。

    3. 总结

    引入Iterator 设计模式可以将遍历和实现分离开来

    如下即使 DemoAggregate 中实现从数组变成了 List 或者其他自定义结构,在这里的遍历都不需要修改:

    	public class IteratorDemoMain {
    	    public static void main(String[] args) {
           		final Aggregate demoAggregate = new DemoAggregate();
           		
    			// 这里的遍历完全不再依赖于 DemoAggregate 
    	        final Iterator<String> iterator = demoAggregate.iterator();
    	        while (iterator.hasNext()){
    	            final String next = iterator.next();
    	            System.out.println("next = " + next);
    	        }
    	    }
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    相关的设计模式 :

    • Visitor 模式:Iterator 模式 是从集合中一个一个取出元素进行遍历,但是并没有在 Iterator 接口中声明对取出的元素进行何种处理。Visitor 模式则是在遍历元素集合的过程中对元素做相同的处理。因为在遍历集合的过程中对元素进行固定的处理是常用的需求。Visitor 模式正是为了应对这种需求而出现的,在访问元素集合的过程中对元素进行相同的处理,这种模式就是 Visitor 模式。
    • Composite 模式:Composite 模式是具有递归结构的木事,在其中使用 Iterator 模式比较困难
    • Factory Method 模式:在 iterator 方法中生成 Iterator 的实例时可能会使用 Factory Method 模式

    三、Adapter 模式

    Adapter 模式 :加个适配器以便于复用

    1. 介绍

    Adapter 模式也被称为 Wrapper模式(包装器模式),适配器模式有两种:

    • 类适配器模式(使用继承的适配器): 类图如下,由实现了 Target 接口 (适配器接口)的 Adapter (适配器类) 通过继承 Adaptee (被适配的类)的方式来对 Adaptee 进行适配。在实际使用时暴露给外界的是 Target 接口,在 Target 的方法中则实现了对 Adaptee 的适配。
      在这里插入图片描述

    • 对象适配器模式(使用委托的适配器):类图如下,Adapter (适配器)通过持有 Adaptee(被适配的类) 来进行适配,外界调用 Target(适配器接口)的方法时实际上则是通过 Adapter 对 Adaptee 进行了适配后的结果。
      在这里插入图片描述

    Adapter 模式中登场的角色有:

    • Target (对象) :该角色负责定义而所需的方法。
    • Client (请求者) :该角色负责使用 Target 角色所定义的方法进行具体处理。
    • Adaptee (被适配) :Adaptee 是一个持有既定方法的角色。
    • Adapter (适配) :使用Adaptee角色的方法来满足 Target 角色的需求。

    1.1 类适配器模式

    该模式意在通过继承 Adaptee 的方式来对其进行适配, 如下:

    
    	public interface Target {
    	    void targetMethodA();
    	}
    	
    	public class Adaptee {
    	    public void methodA(){
    	        System.out.println("Adaptee.methodA");
    	    }
    	}
    	
    	public class Adapter extends Adaptee implements Target {
    	    @Override
    	    public void targetMethodA() {
    	        System.out.println("Adapter.targetMethodA");
    	        methodA();
    	    }
    	}
    	
    	public class Client {
    	    public static void main(String[] args) {
    	        Target target = new Adapter();
    	        target.targetMethodA();
    	    }
    	}
    
    
    • 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

    1.2 对象适配器模式

    	public class Adaptee {
    	    public void methodA() {
    	        System.out.println("Adaptee.methodA");
    	    }
    	}
    	
    	public interface Target {
    	    void targetMethodA();
    	}
    	
    	
    	public class Adapter implements Target {
    	
    	    private Adaptee adaptee;
    	
    	    public Adapter(Adaptee adaptee) {
    	        this.adaptee = adaptee;
    	    }
    	
    	    @Override
    	    public void targetMethodA() {
    	        System.out.println("Adapter.targetMethodA");
    	        adaptee.methodA();
    	        // TODO : do something
    	    }
    	}
    	
    	public class Client {
    	    public static void main(String[] args) {
    	        Target target = new Adapter();
    	        target.targetMethodA();
    	    }
    	}
    
    • 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

    2. 应用

    下面我们来看 适配器模式在现实中的应用,如下:

    2.1 AdvisorAdapter

    Spring Aop 是通过 Advice 来增强代理类的功能, 在 Advice 的基础上创建了多个子接口和实现类,BeforeAdvice (方法执行前执行)、AfterAdvice(方法执行后执行) 等。如下图:
    在这里插入图片描述

    实际上Spring 需要 MethodInterceptor 来完成方法拦截。因此,对于 MethodBeforeAdvice、ThrowsAdvice、AfterReturningAdvice 类型本身并非是 MethodInterceptor 类型的 Advice 增加了适配器来转换为对应的 MethodInterceptor (MethodBeforeAdvice 转为 MethodBeforeAdviceInterceptor,ThrowsAdvice 转为 ThrowsAdviceInterceptor,AfterReturningAdvice 转为 AfterReturningAdviceInterceptor)。

    Spring Aop 的完整分析流程如有需要详参:Spring源码分析十二:@Aspect方式的AOP上篇 - @EnableAspectJAutoProxy


    如下 AfterReturningAdviceAdapter 将 AfterReturningAdvice 转换为 AfterReturningAdviceInterceptor 类型:

    class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
    
    	@Override
    	public boolean supportsAdvice(Advice advice) {
    		return (advice instanceof AfterReturningAdvice);
    	}
    
    	@Override
    	public MethodInterceptor getInterceptor(Advisor advisor) {
    		AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
    		return new AfterReturningAdviceInterceptor(advice);
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.2 HandlerAdapter

    在 Spring Mvc 中 由于有多种 HandlerMapping (处理器映射),他们会依据各自的规则扫描并将合适的方法作为自己的处理器。如 RequestMappingHandlerMapping 就是我们常见的会扫描容器中 被 @ResquestMapping 注解修饰的方法并封装成 handler,当我们发起 http 调用如果调用地址匹配到了 RequestMappingHandlerMapping 中的 handler, 则会直接调用handler 来处理该次请求。这便是简化版的 Spring mvc 流程。由于 HandlerMapping 有多种就存在多种 handler,Spring mvc 为了兼容这种情况,则 通过 HandlerAdapter 来对不同的 Handler进行适配。

    Spring mvc 完成的分析流程如有需要详参: Spring源码分析二十二:Spring MVC③ DispatcherServlet的逻辑


    如下是 HandlerAdapter 的定义:

    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

    在 DispatcherServlet#getHandlerAdapter 中会获取handler 对应的适配器,在后面通过HandlerAdapter#handle 来处理对应的handler。

    	// org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter
    	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    		if (this.handlerAdapters != null) {
    			for (HandlerAdapter adapter : this.handlerAdapters) {
    				if (adapter.supports(handler)) {
    					return adapter;
    				}
    			}
    		}
    		throw new ServletException("No adapter for handler [" + handler +
    				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3. 总结

    • 类适配器模式 : 这种情况感觉和装饰器模式非常类似,区别在于装饰器模式会重写对应的方法来扩展实现,而类适配器模式会直接通过一个新方法来扩展实现。个人一般都使用装饰器模式来解决问题。
    • 对象适配器模式:该模式个人在日常中使用的非常多,尤其是在面对扩展场景时,使用适配器模式可以很好的遵守【开闭原则】,并且代码也很清晰。

    相关的设计模式 :

    • Bridge 模式:Adapter 模式用于连接接口(API)不同的类,而 Bridge 模式则用于连接类的功能层次结构与实现层次的结构。
    • Decorator 模式:Adapter 模式用于填补不同接口 (API)之间的缝隙,而 Decorator 模式则是在不改变接口的前提下增加功能。

    以上:内容部分参考
    《 图解设计模式》
    https://blog.csdn.net/qq_37774171/article/details/122643900#t9
    如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正

  • 相关阅读:
    java高级---IO流
    Cookie与Session
    2022-Aug-28
    # Java手写LRU缓存算法
    Convert转换学习
    txt大文件拆分(批量版)
    反序列化漏洞(1), 原理, 实验, 魔术方法
    milvus upsert流程源码分析
    Flink Sink
    elementUI 年份范围选择器实现
  • 原文地址:https://blog.csdn.net/qq_36882793/article/details/125927410