• Java设计模式(上)


    参考文献:《Head First 设计模式》【美国】弗里曼

    1)设计原则

    模式:就是在某种情景下,针对某问题的某种解决方案

    • 抽象原则:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
    • 组合原则:多用组合,少用继承(Java中是单继承)。
    • 面向接口编程原则:针对接口编程,而不是针对实现编程(Spring中的ApplicationContext高级展现形式就体现了该原则)。
    • 松耦合编程原则:为了交互对象之间的松耦合设计而努力。
    • 开闭原则:类应该对扩展开放,对修改关闭。
    • 依赖倒置原则:依赖抽象,不要依赖具体类。
    • 最少知识原则:只和你的密友谈话。
    • 好莱坞原则:别调用(打电话)我们,我们会调用(打电话)给你。
    • 单一责任原则:一个类应该只有一个引起变化的原因(高内聚:用来度量一个类或模块紧密地达到单一目的或责任)。类的每个责任都有改变的潜在区域,超过一个责任,则意味着超过一个改变的区域。当一个模块或一个类被设计成只支持一组相关的功能时,我们称之为该类是高内聚的。

    2)策略模式

      策略模式定义了算法蔟,分别封装起来,让他们之间可以相互转化,此模式让算法的变化独立于使用算法的用户。

      策略模式的核心在于面向接口编程,类似一个鸭子对象,有飞和叫两个动作,但不同的鸭子实现方式是不同的。这时候就需要使用策略模式来对不同的鸭子做出不同的响应。

    /*
    Spring中策略模式的体现:AbstractAutowireCapableBeanFactory.createBeanInstance(), 不同的beandefinition会选择不同的策略来创建bean实例
    */
    protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    	// 创建bean 实例, 这里会创建Spring 自带的几个bean 实例, 也会创建自己定义的bean, 先传进来的是Spring自带的
    	Class<?> beanClass = resolveBeanClass(mbd, beanName);
    
    	if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
    		throw new BeanCreationException(mbd.getResourceDescription(), beanName,
    				"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
    	}
    
    	// 如果存在Supplier (接口)回调, 则调用obtainFromSupplier() 进行初始化
    	Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
    	if (instanceSupplier != null) {
    		return obtainFromSupplier(instanceSupplier, beanName);
    	}
    
    	// 如果工厂方法不为空的话, 则使用工厂方法进行初始化策略
    	if (mbd.getFactoryMethodName() != null) { // 工厂方法: 可以简单理解为@configuration注解下的类中, @bean注解定义的Bean, 是方法级别定义的bean对象, 可以理解为工厂方法创建的bean
    		return instantiateUsingFactoryMethod(beanName, mbd, args);
    	}
    
    	// Shortcut when re-creating the same bean...
    	boolean resolved = false;
    	boolean autowireNecessary = false;
    	if (args == null) {
    		synchronized (mbd.constructorArgumentLock) {
    			if (mbd.resolvedConstructorOrFactoryMethod != null) {
    				resolved = true;
    				autowireNecessary = mbd.constructorArgumentsResolved;
    			}
    		}
    	}
    	if (resolved) {
    		if (autowireNecessary) {
    			return autowireConstructor(beanName, mbd, null, null);
    		}
    		else {
    			return instantiateBean(beanName, mbd);
    		}
    	}
    
    	// Candidate constructors for autowiring?
    	Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    	if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
    			mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
    		return autowireConstructor(beanName, mbd, ctors, args);
    	}
    
    	// Preferred constructors for default construction?
    	ctors = mbd.getPreferredConstructors();
    	if (ctors != null) {
    		return autowireConstructor(beanName, mbd, ctors, null);
    	}
    
    	// No special handling: simply use no-arg constructor.
    	return instantiateBean(beanName, mbd);
    }
    
    • 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

    3)观察者模式

      观察者模式是JDK中使用得最多的模式之一,观察者模式定义了对象之间一对多的依赖,这样一来,当一个对象改变状态时,它的所有依赖都会收到通知并自动更新。

      Java内置的观察者模式,java.util.Observable类与java.util.Observer接口,观察者模式类似于VUE中的响应式编程,当一个对象改变状态时,它的依赖(使用它的地方)都会收到通知并自动更新。

    java内置的观察者模式

    4)装饰者模式

      装饰者模式可以动态地将“责任”附加到对象中去,若要扩展功能,装饰者提供了比继承更有弹性的代替方案。

      从某种程度来讲,装饰者模式是对一个对象进行一步步的增强,其中一步步的过程可以理解为对该对象的装饰。类似一杯饮料,根据客户的要求,你可以为它一步步的添加各种小料,最终使它成为一杯符合要求的饮料。

      Java中的IO中就是装饰者模式的代表,InputStream->FilterInputStream->BufferedInputStream,一步步装饰、增强。

    image-20220801140947656

    5)工厂方法模式

      工厂方法模式是简单工厂模式的进一步抽象和推广,它让类的实例化推迟到子类中进行。工厂方法模式定义了一个创建对象的接口,但是要由子类来决定实例化的类是哪一个,把创建对象的操作下发到对象工厂中去完成。

      工厂方法模式常用于解决对象多态的问题,对应对象的创建应当把创建方法与实例化方法解耦,类似现在要做一个披萨,但是这个披萨有很多种类型,在实例化时想根据用户的需求动态的创建披萨,这时候就可以用到工厂方法模式,让工厂来帮助我们创建对象。

    注意:正如前面所说,工厂方法让子类来决定要实例化的类是哪一个,这里所谓的“决定”,并不是指模式运行子类本身在运行时做决定,而是指在编写创建类时,不需要知道实际要创建的产品是哪一个,选择了使用哪个子类自然就决定了实际创建的产品是什么。

    image-20220801141901426

      在Spring中Bean的创建就使用了工厂方法模式,FactoryBean是Spring中的一个接口,常用来获取单例Bean实例。当一个类实现了FactoryBean接口,那么当我们从Spring IOC容器中获取该类的实例时,Spring会调用getObject()方法(如果子类重写了该方法),把方法的返回结果给我们。

    // 定义一个Bean, 实现FactoryBean(工厂模式)接口
    @Component
    public class MyFactoryBean implements FactoryBean {
    
    	String RECORDID;
    
    	@Override
    	public MyFactoryBean getObject() throws Exception {
    		MyFactoryBean myFactoryBean = new MyFactoryBean();
    		myFactoryBean.setRECORDID("通过getObject方法初始化实例=========");
    		System.out.println("MyFactoryBean.getObject()");
    		return myFactoryBean;
    	}
    
    	@Override
    	public Class<?> getObjectType() {
    		System.out.println("MyFactoryBean.getObjectType()");
    		return MyFactoryBean.class;
    	}
    
    	// 省略构造函数、get、set、toString方法
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    // 测试MyFactoryBean工厂模式
    public class MainTest {
    	public static void main(String[] args) {
    		ApplicationContext context = new AnnotationConfigApplicationContext(MainTest.class);
    		MyBean myBean = context.getBean(MyBean.class); // myBean与heBean都是基础bean
    		HeBean heBean = context.getBean(HeBean.class);
    		MyFactoryBean myFactoryBean = context.getBean(MyFactoryBean.class);
    
    		System.out.println(myBean);
    		System.out.println(heBean);
    		System.out.println(myFactoryBean);
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

      我们先看容器启动之后,三个bean的情况,在MyBean myBean = context.getBean(MyBean.class);处打上断点:

    image-20220801152447656

      此时三个bean都已实例化完成,可以看到此时的myFactoryBean是通过构造函数来进行实例化的myFactoryBean -> {MyFactoryBean@2123} "MyFactoryBean{RECORDID='通过构造方法初始化实例=========='}"工厂方法模式让类的初始化推迟到子类中进行。

      此时放开断点,看三个bean的sout方法输出是什么:

    image-20220801152847899

      可以看到此时的myFactoryBean中的值已经被修改了,因为它实现了FactoryBean方法,所以在获取该Bean时,会调用该类中重写的getObject()方法。回到第一步,该方法中就会重新创建Bean,对Bean进行修改。

      所以(Spring)工厂方法模式定义了一个创建对象的接口(FactoryBena),但是要由子类(MyFactoryBean)来决定实例化的类是哪一个,把创建对象的操作下发到对象工厂(实现了FactoryBean接口的类)中去完成。

    6)抽象工厂模式

      抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

      抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道(或关心)实际产出的具体产品是什么,这样一来,客户就可以从他们的具体产品中解耦。抽象工厂中的每个方法实际上都是工厂方法。抽象工厂的任务是定义一个负责创建一组产品的接口,这个接口内的每个方法都负责创建一个具体的产品,同时我们利用实现抽象工厂的子类来提供这些具体的做法。

    抽象工厂与工厂方法的区别:

    • 整个工厂方法模式,不过是通过子类来创建对象(比如创建一个披萨),用这种方法,客户只需要知道他们所使用的抽象类型就可以了,而由子类来负责决定具体的类型。所以工厂方法模式只负责将客户从具体类型中解耦。
    • 抽象工厂模式提供了一个用来创建一个产品家族的抽象类型(比如创建一个披萨,但是还要包含披萨的原材料),这个类型的子类定义了产品被产生的方法。
    image-20220801164906617

    ​ Spring中的BeanFactory就是采用了抽象工厂模式的设计思路,Spring org.springframework.beans.factory.BeanFactory源码:

    /**
     *    BeanFactory 接口中的方法, 其中都是有关于Bean的操作, 要么通过name, 类型的字节码去获取Bean, 要么就是根据名称或者ResolvableType来判断
     * Bean是singleton还是prototype的, 但是这里面没有关于Bean注入的内容, Spring底层提供了Bean注册中心, 而BeanFactory接口主要是声明获取Bean信息的相关方法.
     */
    public interface BeanFactory {
    	// 1. beanFactory 自带的bean 前缀
    	String FACTORY_BEAN_PREFIX = "&";
    	// 2. 根据bean的name获取Bean, Bean的默认name为首字母小写的类名
    	Object getBean(String name) throws BeansException;
    	// 3. 根据bean的name获取Bean, 并转化为指定的对象
    	<T> T getBean(String name, Class<T> requiredType) throws BeansException;
    	// 4. 根据bean的name获取Bean, 并传入构造该bean的参数, 用于有参构造
    	Object getBean(String name, Object... args) throws BeansException;
    	// 5. 根据字节码获取bean
    	<T> T getBean(Class<T> requiredType) throws BeansException;
    	// 6. 根据字节码获取bean, 并传入构造该bean的参数, 用于有参构造
    	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
    	// 7. 根据字节码获取指定bean 的提供者
    	<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
    	// 8. 根据指定的解析类型获取bean 的提供者
    	<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
    	// 9. 判断工厂中是否包含指定bean
    	boolean containsBean(String name);
    	// 10. 根据beanName来判断该bean是否是单例的
    	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    	// 11. 根据beanName来判断该bean是否是原型对象
    	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    	// 12. 根据名称判断Bean 是否能被指定的可解析类型解析
    	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    	// 13. 根据名称判断Bean 是否被指定的字节码对应的类解析
    	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    	// 14. 根据名称获取bean的类型对应的字节码
    	@Nullable
    	Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    	// 15. 根据名称获取Bean 的类型对应的字节码, 并设置是否允许Bean初始化
    	@Nullable
    	Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
    	// 16. 获取Bean 的别名
    	String[] getAliases(String 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

      在Spring中,自带的BeanFactory接口实现类就有大几十个,BeanFactory顶层接口中声明了关于获取Bean信息的方法,其他所有的工厂都是BeanFactory的子类,虽然在BeanFactory中并没有显示的声明具体有哪些工厂,但这些工厂都会来实现BeanFactory接口。这就是抽象工厂模式的设计方法,BeanFactory就相当于是抽象工厂,而其继承子类就相当于产品,不同的工厂类就相当于不同的产品。抽象工厂模式提供一个接口(BeanFactory),用于创建相关或依赖对象的家族,而不需要明确指定具体类。

    image-20220801165329082

    7)单例模式

      单例模式可以确保一个类只有一个实例,并提供一个全局的访问点。

      单例模式常用来管理共享的资源,保证操纵的都是同一个对象,例如数据库连接池、线程池、日志对象等。经典单例模式案例分为饿汉式与懒汉式单例,需要注意的是要在多线程环境下考虑线程安全问题。

    // 单例模式-饿汉式
    public class SingletonModeHungry {
        private static final SingletonModeHungry instance = new SingletonModeHungry();
        // 饿汉式单例模式实现, 使用之前就创建好实例, 等到需要使用时直接返回该实例即可
        public SingletonModeHungry getInstance() {
            return instance;
        }
    }
    
    // 单例模式-懒汉式
    public class SingletonModeLazy {
        private SingletonModeLazy instance = null;
    	// 懒汉式单例模式实现, 需要使用该实例时, 再去实例化
        public SingletonModeLazy getInstance() {
            synchronized (instance) {
                if (instance == null) {
                    synchronized (instance) {
                        instance = new SingletonModeLazy();
                    }
                }
                return instance;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

      Spring中的单例模式思想:在Spring中,Bean默认都是单例的,在创建bean实例时,Spring首先会判断该bean是否是单例的,如果是单例的则会执行一系列单例bean的实例化方法,最后将该bean实例放入IOC容器(singletonObjects)中,这样从每次从容器中取出的bean实例都是同一个。

    // DefaultSingletonBeanRegistry.getSingleton(String beanName, ObjectFactory singletonFactory)
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        ...
        // heavyHead TODO 2022/7/21 : 单例模式 -> 如果成功创建了目标单例bean, 则将创建出来的目标bean 添加到singletonObjects(SpringIOC) 容器中
        if (newSingleton) {
        	addSingleton(beanName, singletonObject);
        }
        return singletonObject;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    8)命令模式

      命令模式将请求封装成对象,以便于使用不同的参数表示不同的请求。

      命令模式可以将“动作的请求者”从“动作的执行者”对象中解耦,让对象之间的调用关系更加灵活,类似餐厅中服务员与厨师就是一种命令模式的展现,服务员负责提供用户点菜的菜单,将用户点好的菜单提供给厨师,由厨师来负责做菜,二者之间都不需要对象实现的细节(服务员不需要了解菜是怎么做的,厨师不需要了解菜是怎么点的),就可以将工作完成。

      Spring中命令模式思想:Spring框架中的JdbcTemplate

    // JdbcTemplate.query() 方法
    @Override
    @Nullable
    public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
    	...
        // query() 方法中定义了一个内部类QueryStatementCallback, 并实现了StatementCallback接口
    	class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
    		@Override
    		@Nullable
    		public T doInStatement(Statement stmt) throws SQLException {
    			ResultSet rs = null;
    			try {
    				rs = stmt.executeQuery(sql);
    				return rse.extractData(rs);
    			}
    			finally {
    				JdbcUtils.closeResultSet(rs);
    			}
    		}
    		@Override
    		public String getSql() {
    			return sql;
    		}
    	}
        // 最后的execute执行方法, 其实就是将内部类QueryStatementCallback(内部类)作为参数进行返回, 在这里QueryStatementCallback就相当于命令模式中的具体命令对象, 而StatementCallback(接口)则是抽象命令对象, 该接口还有其他具体命令实现类: BatchUpdateStatementCallback/ExecuteStatementCallback/UpdateStatementCallback, 该接口不同的实现类就代表着不同的命令
        // 将请求封装成对象,以便于使用不同的参数表示不同的请求
    	return execute(new QueryStatementCallback());
    }
    
    // execute(...) 方法具体查看 JdbcTemplate.execute(StatementCallback action)
    
    // StatementCallback 接口
    @FunctionalInterface
    public interface StatementCallback<T> {
        @Nullable
    	T doInStatement(Statement stmt) throws SQLException, DataAccessException;
    }
    
    • 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

    9)适配器模式

      适配器模式将一个类的接口,转换为用户期望的另一个接口,适配器可以让原本接口不兼容的类可以合作无间。

      通俗来讲,适配器是在两个接口之间搭建起了一座桥梁让他们之间可以相互转换,让不兼容的接口变成兼容,这可以让用户从实现的接口中解耦。

      SpringMVC中的适配器模式主要用于执行目标Controller中的请求处理方法,其中DispatcherServlet作为用户,HandlerAdapter作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller则作为需要适配的类。

      SpringMVC中Controller种类众多,不同类型的Controller要通过不同的方法来对请求进行处理,如果不使用适配器模式,那DispatcherServlet需要直接获取对应类型的Controller,需要自行来判断:

    if(mappedHandler.getHandler() instanceof MultiActionController){
     ((MultiActionController)mappedHandler.getHandler()).xxx  }else if(mappedHandler.getHandler() instanceof XXX){  
        ...  
    }else if(...){  
       ...  
    }  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

      这种情况下,每当我们新增一个Handler时,都需要在上面这个if块中新增一个分支,这种形式既使得程序难以维护,也违反了开闭原则-对扩展开放、对修改关闭。

    // org.springframework.web.servlet.HandlerAdapter 接口
    public interface HandlerAdapter {
        boolean supports(Object var1);
    
        @Nullable
        ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
    
        long getLastModified(HttpServletRequest var1, Object var2);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

      现该接口的适配器每个Controller都有一个适配器与其相对应,这样一来,每自定义一个Controller都需要定义一个实现HandlerAdapter接口的适配器。

    SpringMVC中默认提供的Controller实现类如下:

    image-20220813114417668

    SpringMVC中默认提供的HandlerAdapter实现类如下:

    image-20220813114516353

    org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter 实现类源码:

    // org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter
    public class HttpRequestHandlerAdapter implements HandlerAdapter {
    	@Override
    	public boolean supports(Object handler) {
    		return (handler instanceof HttpRequestHandler);
    	}
    
    	@Override
    	@Nullable
    	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
    			throws Exception {
    
    		((HttpRequestHandler) handler).handleRequest(request, response);
    		return null;
    	}
    
    	@Override
    	public long getLastModified(HttpServletRequest request, Object handler) {
    		if (handler instanceof LastModified) {
    			return ((LastModified) handler).getLastModified(request);
    		}
    		return -1L;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

      当Spring容器启动之后,会将所有定义好的适配器对象都存放在一个List集合中,当一个请求来临时,DispatcherServlet会通过handler的类型找到对应的适配器,并将该适配器对象返回给用户,然后就可以通过适配器的hanle()方法来调用Controller中的用于处理请求的方法。

    // org.springframework.web.servlet.DispatcherServlet
    @SuppressWarnings("serial")
    public class DispatcherServlet extends FrameworkServlet {
    
        /** List of HandlerAdapters used by this servlet. */
    	@Nullable
    	private List<HandlerAdapter> handlerAdapters; // 所有的适配器对象都会放入该集合中
    	
        // 处理程序的实际调度, 分发请求
        protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        try {
            ...
            // 确定当前请求的处理程序适配器 (将HandlerAdapter适配为我们需要的)
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
            ...
        	// 实际上调用处理程序。
        	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
        	...
        }
    }
    // 通过适配器模式, 我们可以将所有的Controller统一交给HandlerAdapter去处理, 这样避免了大量的控制器判断语句, 也更利于扩展新的Controller类型
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    10)外观模式

      外观模式提供了一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层接口,让子系统更容易使用。

      外观模式的意图在于要提供一个简单的接口,好让一个子系统能够更易于使用,就是一系列的操作统一起来,抽象成一个新的外观接口,去控制一系列的操作,达到松耦合的目的。

    适配器模式的意图在于“改变”接口以符合客户的期望;而外观模式的意图是,提供子系统的一个简化接口。

    image-20220815105623651

      Mybatis中的外观模式思想:

    // org.apache.ibatis.session.Configuration
    public class Configuration {
        ...
        // Configuration类中定义了三个默认的接口实现类, 后续创建MetaObject 对象时会用到
    	protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
    	protected ObjectFactory objectFactory = new DefaultObjectFactory();
    	protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
        
        // 在创建MetaObject对象时, 实现定义了多个接口作为参数, 通俗来说提供了一个统一的入口(接口), 用来访问子系统中的一群接口, 将它们都统一起来, 去控制一系列的操作, 松耦合
    	public MetaObject newMetaObject(Object object) {
    		return MetaObject.forObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
    	}
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    11)模板方法模式

      模板方法模式会在一个方法中定义一个算法的骨架,将一些具体的步骤延迟到子类,模板方法可以使得子类在不改变算法的结构的情况下,重新定义算法中的某些步骤。

      模板就是一个方法,更准确的说这个方法将算法定义为一组有序步骤,任何步骤都可以是抽象的,由子类负责实现,这样可以保证算法的结构不受改变。例如我们一天中要起床、吃饭、睡觉,每个人起床和睡觉都是一样的,我们就可以把这两步抽象出来放在模板方法中去实现,而每个人吃饭不一定相同,所以就将这步下推到各个子类中自己去实现。

    模板方法的思想类似于钩子函数的思想:

    ​ 钩子函数是一种被声明在抽象类中的方法,但只有空的或默认的函数实现,钩子的存在可以让子类有能力对算法的不同的点进行挂钩。

    ​ 钩子函数存在的意义在于底层组件能够被挂钩进计算中,而且又不会让高层组件过于依赖低层组件,与依赖倒置原理类似,目的都在于解耦。钩子函数被广泛运用于生命周期函数(Vue组件生命周期函数)、模板方法、框架中。

      Vue中的模板方法模式思想,定义了一系列的vue实例生命周期方法,在不同的时机会回调不同的钩子函数方法,而这些钩子函数方法本身没有任何的实现,具体的工作延迟到子类中定义:

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    
    
    • 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

    12)迭代器模式

      迭代器模式提供了一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

      迭代器模式让我们能够游走于集合内的每个元素,而又不会暴露其内部的表示,把游走的任务放在迭代器上,而不是聚合上。这样简化了集合的接口与实现,也让责任各得其所。Jdk中的Iterator迭代器就是使用到了这种迭代器模式。

    // I java.util.function.Consumer.Iterator
    public interface Iterator<E> {
        // 需要使用迭代器的集合/容器会实现该接口, 然后重写其中的hasNext()/next()...方法, 以此来达到顺序访问一个聚合对象中的各个元素的目的
        boolean hasNext();
        E next();
        default void remove() {
            throw new UnsupportedOperationException("remove");
        }
        default void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            while (hasNext())
                action.accept(next());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    13)组合模式

      组合模式允许将对象结合成为树形结构(层次结构),来表现“整体/部分”的层次结构,组合能够让用户以一致的方式来处理个别对象一以及对象组合。

      注意组合模式中提到的树形结构,并不是我们Java语言中数据结构的那种,而是指逻辑上的树形结构,类似Vue中的组件树概念。组合模式能让我们以一致的方式处理对象主要体现在我们首先定义一个主接口,然后让所有的对象都来实现该主接口,对于你是什么类型、有什么方法我并不关心,你只需要实现你有的方法即可,作为方法的调用者,甚至不需要关心个体的差异带来方法上的差异,直接一视同仁即可。

      组合模式的优势在于,可以让用户的代码(层次)上更简单,用户可以不需要关注此时面对的是组合对象还是叶子节点对象,也不需要去写很多的判断语句去保证他们对正确的对象调用了正确的方法,通常他们只需要对整个结构调用同一个方法并执行操作就可以了。

      SpringMVC中组合模式思想:RequestMappingHandlerAdapter用来处理多样化参数映射问题、返回值映射等问题,其中就使用到了组合模式。

    // org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
    public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {
        ...
        // 抽象容器定义行为, 并将所有的树枝组件串联起来, 形成树形结构(层次结构)
        @Nullable
        private List<HandlerMethodArgumentResolver> customArgumentResolvers;
        // 树枝组件将叶子组件全都连接起来
        @Nullable
        private HandlerMethodArgumentResolverComposite argumentResolvers;
        @Nullable
        private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

      抽象组件HandlerMethodArgumentResolver接口定义了参数的解析行为,Spring为我们提供了很多不同作用的实现类,用于不同场景下的解析工作,这些在使用过程中都属于叶子组件(真正干活的)。

    image-20220815151649818

      HandlerMethodArgumentResolverComposite 作为树枝组件,此组件统一对参数解析这一行为的调用(高层模块调用简单)。

    14)状态模式

      状态模式允许对象在内部状态改变它的行为,使对象看起来好像修改了它的类。

      状态模式允许一个对象基于内部的状态而拥有不同的行为,这点与策略模式很类似,二者不同的地方在于影响他们的点不同。以往在处理不同的状态时,程序要做出不同的反应,通过条件判断来做分支处理判断对象当前的状态从而去做出不同的响应,状态模式的思想是不要将行为封装成类,而是要将状态封装成类,从而在类中处理行为。

      状态模式和策略模式很相似,它也能解决多层 if-else 嵌套的问题。但是它们的意图不太一样,策略模式会控制对象使用什么策略,而状态模式会自动改变状态。

      TCP的三次握手、四次挥手机制就是一种状态模式的体现:

    image-20220815225237238 image-20220815225313765

    15)代理模式

      代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。

      使用代理模式创建代表对象,让代表对象控制某对象的访问,被代理的对象可以是远程的对象、创建开销打的对象或需要安全控制的对象。JVM句柄池中就有一个这样的概念,当大对象还未创建完成时,虚拟代理就会提前为我们接收请求,待对象创建完毕之后,就会将请求委托给真正的对象。

    装饰者为对象增加行为,而代理则是控制对象的访问。

      代理模式又分为静态代理与动态代理,具体实现查看Mybatis笔记中的代理模式。

    16)复合模式

      复合模式会结合两个或两个以上的模式,从而组成一个解决方案,解决一再发生的一般性问题。

      MVC模式就是最著名的复合模式之一,视图和控制器实现了经典的策略模式:视图是一个对象,可以被调整使用不同的策略,控制器则提供了策略;视图又使用了组合模式,每个显示的组件如果不是组合节点那就是叶子节点;模型则实现了观察者模式,当状态发生改变时,相关对象将会持续性更新。

  • 相关阅读:
    java异常 | 处理规范、全局异常、Error处理
    自动售货机
    java计算机毕业设计校园环境保护监督系统源代码+系统+数据库+lw文档
    Thinkphp5萤火商城B2C小程序源码
    【Python程序设计】 工厂模式【07/8】
    015-JAVA类与对象详细介绍
    新手怎样快速上手接口测试?掌握这几个知识点直接起飞!
    Numpy学习
    私域增长 | 私域会员:9大连锁行业15个案例集锦
    机器语言编写helloworld
  • 原文地址:https://blog.csdn.net/zy_zhangruichen/article/details/126356466