【设计模式系列】1.设计模式概述

一、什么是设计模式?

1、概念

  • 是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结;
  • 一共有23种设计模式,总体来说可以分为三大类:创建型模式( Creational Patterns )、结构型模式( Structural Patterns )和行为型模式( Behavioral Patterns ),如图所示:
    在这里插入图片描述

2、六大设计原则

  • 开闭原则:程序实体对拓展开放,对修改关闭,扩展功能的时候应该尽量的不修改原有的代码。
  • 单一职责原则:一个类只负责一个功能领域的相应职责。
  • 接口隔离原则:使用多个专门的接口,而非单一的总接口,便于维护,降低耦合度。
  • 依赖倒置原则:针对接口编程,依赖于抽象而不依赖于具体。
  • 里氏替换原则:子类可以拓展父类的功能,但不能改变父类原有的功能。体现了开闭原则。
  • 迪米特法则:一个对象尽可能少的与其他对象发生相互作用,体现了松耦合思想。

二、设计模式有哪些优点?

优点如下

  • 增强代码的复用性与结构性;
  • 增强持续的健壮性、耦合性(高内聚低耦合);
  • 有利于软件升级和改造;
  • 增强工程的扩展性;

三、JDK中常用的设计模式概述

1.单例模式

java.lang.Runtime#getRuntime();
java.awt.Desktop#getDesktop();
java.awt.Toolkit#getDefaultToolkit();

2.工厂模式

java.lang.Boolean#valueOf(String);
java.lang.Object#toString();
java.lang.Class#forName();
java.lang.Class#newInstance();
java.lang.reflect.Constructor#newInstance();
java.lang.reflect.Array#newInstance();
java.lang.Proxy#newProxyInstance();

3.代理模式

jdk动态代理(java.lang.reflect.Proxy);
远程方法调用(RMI)

4.观察者模式

java.util.EventListener;
javax.servlet.http.HttpSessionBindingListener;
javax.servlet.http.HttpSessionAttributeListener;

5.模板模式

java.util.Collections#sort();
java.io.InputStream#read();
java.util.AbstractList#indexOf();

6.责任链模式

javax.servlet.Filter#doFilter;
java.util.logging.Logger#log();

7.策略模式

java.util.Comparator#compare();
javax.servlet.http.HttpServlet;
javax.servlet.Filter#doFilter();

8.外观模式

java.lang.Class;
javax.faces.webapp.FacesServlet;

9.适配器模式

java.util.Arrays#asList();
java.io.InputStreamReader(InputStream);
java.io.OutputStreamWriter(OutputStream);

四、常用框架有关的设计模式(重点)

前言

后端常用的框架有Spring/SpringMVC/SpringBoot,Mybatis/Hibernate等,涉及到的设计模式也是工作常用的和面试常问的,像Spring框架涉及到的涉及模式有9种之多,因此这一部分显得格外重要,重点来整理说明。

1、Spring框架常用的设计模式

①.单例模式

含义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。比如说:线程池、缓存、对话框、注册表、日志记录等等。

Spring 中的单例模式:Spring 创建的 bean 默认作用域就是 singleton(单例)的,只是提供了全局的访问点BeanFactory,而Spring要通过IOC容器去实现管理任意Java对象,所以没有从构造器级别去控制单例。

Spring 通过注解或XML方式来使用单例:

# 注解:
@Scope(value = "singleton")
# XML:
<bean id="orderService" class="com.it.xxwei.service.OrderService" scope="singleton"/>

当然,除了 singleton 作用域,Spring 中 bean 还有下面几种作用域:

  • prototype : 每次请求都会创建一个新的 bean 实例;
  • request : 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效;
  • session : 每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效;
  • global-session: 全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经没有了。

值得注意的是,Spring 通过 ConcurrentHashMap 实现单例注册表的特殊方式去实现单例模式,核心源码参考如下:

// 通过 ConcurrentHashMap(线程安全) 实现单例注册表
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "'beanName' must not be null");
        synchronized (this.singletonObjects) {
            // 检查缓存中是否存在实例  
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                //...省略了很多代码
                try {
                    singletonObject = singletonFactory.getObject();
                }
                //...省略了很多代码
                // 如果实例对象在不存在,我们注册到单例注册表中。
                addSingleton(beanName, singletonObject);
            }
            return (singletonObject != NULL_OBJECT ? singletonObject : null);
        }
    }
    //将对象添加到单例注册表
    protected void addSingleton(String beanName, Object singletonObject) {
            synchronized (this.singletonObjects) {
                this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));

            }
        }
}

②.工厂模式

核心思想:有一个专门的类来生产其他类的实例,生产的这些实例有一个共同父类。Spring使用工厂模式可以通过 BeanFactory 或 ApplicationContext 创建 bean 对象。那么,BeanFactory 或 ApplicationContext 有何区别呢?

  • ApplicationContext :容器启动的时候,不管用没用到,会一次性创建所有 bean 。
  • BeanFactory:延迟注入,即用到某个bean才去注入,相比于ApplicationContext 而言它会占用更少的内存,程序启动速度更快。

因此,BeanFactory 只是提供基础的依赖注入支持,而ApplicationContext 扩展了 BeanFactory ,实际中使用ApplicationContext较为普遍,需要注意的是ApplicationContext的三个实现类比较重要:

  • ClassPathXmlApplication:把上下文文件当成类路径资源;
  • FileSystemXmlApplication:从文件系统中的 XML 文件载入上下文定义信息;
  • XmlWebApplicationContext:从Web系统中的XML文件载入上下文定义信息;

③.模板模式

含义:它定义一个操作中的算法的骨架,而将一些步骤延迟到子类中实现。

Spring 中 JdbcTemplate、RedisTemplate、RabbitTemplate等以 Template 结尾的类,它们就使用到了模板模式。通常我们使用到的是Java的继承机制实现模板模式,但Spring 并没有使用这种方式,而是使用Callback 模式与模板方法模式配合,既达到了代码复用的效果,同时增加了灵活性。

④.代理模式

含义:为其他对象提供一种代理,以控制对这个对象的访问。代理模式可分为:静态代理动态代理,前者在程序运行前就已经存在的编译好的代理类,后者运行时由JVM来实现)。

而动态代理又可以分为:JDK动态代理CGLIB动态代理,二者的区别为JDK动态代理产生的代理类和目标类实现了相同的接口,CGLIB动态代理产生的代理类是目标对象的子类。

Spring AOP 就是基于动态代理的,如下图所示,当代理类和目标类实现了相同的接口时使用JDK动态代理,而对于没有实现接口的对象,则通过CGLIB动态代理生成目标对象的子类来代理。

在这里插入图片描述

另外,所谓的AOP,即是面向切面编程,能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。比如,常用的日志功能,事务管理,权限控制等场景。

Spring AOP是基于AspectJ,AspectJ 相比于 Spring AOP 功能更加强大,但 Spring AOP 相对来说更简单。如果所用切面不多,两者的性能差异不明显,当切面太多的话,最好选择 AspectJ ,它比Spring AOP 快很多。二者区别一下:Spring AOP 属于运行时增强,基于动态代理的,而 AspectJ 是编译时增强,基于字节码操作

⑤.装饰器模式

含义:在不改变原有对象功能的基础上添加或修改新的功能,这样可以动态地给对象添加一些额外的属性或行为。

Spring 中配置 DataSource 的时候,有时候需要动态切换数据源,满足连接不同类型数据库的需求,可以参考:Spring主从数据库的配置和动态数据源切换原理

⑥.适配器模式

含义:将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作。

Spring AOP 的增强或通知(Advice)使用到了适配器模式,与之相关的接口是AdvisorAdapter。

Spring MVC 的前端控制器 DispatcherServlet 也使用到了适配器模式,DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler链。解析到对应的 Handler 后,开始由HandlerAdapter 适配器处理。HandlerAdapter 作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller 作为需要适配的类。

在这里插入图片描述

⑦.观察者模式

含义:是一种对象与对象之间具有依赖关系,当一个对象发生改变的时候,这个对象所依赖的对象也会做出反应。

Spring 事件驱动模型用到的就是观察者模式,该模型有三种角色: 事件角色(ApplicationEvent)、事件监听者角色(ApplicationListener)、时间发布者角色(ApplicationEventPublisher)。

Spring 中默认存在以下事件,他们都是对 ApplicationContextEvent 的实现:

  • ContextStartedEvent:ApplicationContext 启动后触发的事件;
  • ContextStoppedEvent:ApplicationContext 停止后触发的事件;
  • ContextRefreshedEvent:ApplicationContext 初始化或刷新完成后触发的事件;
  • ContextClosedEvent:ApplicationContext 关闭后触发的事件。

事件监听者角色ApplicationListener,它是一个接口,里面只定义了一个 onApplicationEvent()方法来处理事件 ApplicationEvent,在 Spring中只要实现 ApplicationListener 接口重写 onApplicationEvent() 方法即可完成监听事件。

事件的发布者 ApplicationEventPublisher 也是一个接口,该接口的publishEvent() 方法在 AbstractApplicationContext 类中被实现,实际上事件真正是通过ApplicationEventMulticaster来广播出去的。

最后,总结一下Spring 的事件流程:

  1. 定义一个事件: 实现一个继承自 ApplicationEvent,并且写相应的构造函数;
  2. 定义一个事件监听者:实现 ApplicationListener 接口,重写 onApplicationEvent() 方法;
  3. 使用事件发布者发布消息: 可以通过 ApplicationEventPublisher的 publishEvent() 方法发布消息。

2、Mybatis框架常用的设计模式

这里只提及一下有哪些设计模式,不再做具体分析了。

单例模式:ErrorContext和LogFactory。

工厂模式:SqlSessionFactory、ObjectFactory、MapperProxyFactory。

代理模式:Mybatis实现的核心,比如MapperProxy、ConnectionLogger,用的jdk的动态代理;还有executor.loader包使用了cglib或者javassist达到延迟加载的效果。

模板模式:例如BaseExecutor和SimpleExecutor,还有BaseTypeHandler和所有的子类例如IntegerTypeHandler。

适配器模式:例如Log的Mybatis接口和它对jdbc、log4j等各种日志框架的适配实现。

Builder模式:例如SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder。

装饰者模式:Cache包中的cache.decorators子包中等各个装饰者的实现。

迭代器模式:PropertyTokenizer。

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值