一、设计模式须知
- 设计模式的作用
- 代码复用:例如单例模式、工厂模式
- 增强代码拓展性:例如策略模式、观察者模式
- 提升代码稳定性和可靠性:例如装饰器模式、迭代器模式
- 提升代码规范性,降低复杂度
- 单例、工厂、策略、观察者、装饰器、代理、建造者模式是23种设计模式中比较常用于面试的
二、常见设计模式
-
单例模式:
-
什么是单例模式:一个类有且只有一个实例,提供一个全局访问方法访问该实例
-
优缺点:
- 优点:
- 节约系统资源开销
- 缺点:
- 拓展性差,单例类的构造函数都是私有的,只有自己可以实例化
- 优点:
-
场景例子:
- IOC容器(依赖注入的bean)默认都是单例的
- 使用@Bean定义一个自定义线程池,避免线程池资源被滥用
-
懒汉式单例模式:
/** * @Description: 懒汉单例模式 * @Author: zxb * @Date: 2025/10/21 12:04 * @Version: 1.0 **/ public class LazySingleton { private volatile static LazySingleton instance = null; private LazySingleton() { } /*** * 双重校验锁(DCL,Double-check Locking) * 双重校验锁是保证多线程环境下单例类的线程安全问题 * 第一层校验是无锁检查,防止实例已经创建还进入同步代码块加锁,减少锁竞争带来的性能消耗 * 第二层校验是防止多线程环境下存在多个线程同时进入同步代码块,导致重复创建实例 * 实例使用volatile的作用是防止指令重排,因为new LazySingleton()创建实例时实际由三条字节命令完成 * step1:分配内存空间 * step2:初始化对象 * step3:将对象的符号引用替换为实际引用(指向分配的内存空间) * 由于JVM可能会对不影响最终结果的指令进行重新排序,可能导致step3在step2之前完成,导致其他线程拿到未完全初始化的对象实例 **/ public static LazySingleton getInstance() { if (instance == null) { synchronized (LazySingleton.class) { if (instance == null) { instance = new LazySingleton(); } } } return instance; } } -
饿汉式单例模式:
/** * @Description: 饿汉单例模式 * @Author: zxb * @Date: 2025/10/21 12:08 * @Version: 1.0 **/ public class EagerSingleton { private static final EagerSingleton instance = new EagerSingleton(); private EagerSingleton() { } public static EagerSingleton getInstance() { return instance; } }
-
-
工厂模式:
- 什么是工厂模式:核心是封装对象的创建逻辑,使调用方无需直接依赖具体类
- 优缺点:
- 优点:
- 解耦对象的创建和使用,统一管理对象的生命周期
- 提高代码的复用性
- 便于维护和拓展
- 缺点:
- 需要编写额外的工厂类和抽象接口
- 优点:
- 场景例子:
- Spring上下文ApplicationContext(动态获取Bean、动态读取配置文件)、BeanFactory
-
策略模式:
-
什么是策略模式:将多个算法封装起来,支持在代码运行过程中根据需要动态切换算法;解决多种相似算法存在时,替换if...else判断,更加简洁,维护性更好
-
优缺点:
-
优点:
- 支持在代码运行时动态切换算法,维护性和拓展性更好
-
缺点:
- 策略类数量可能过多,简单场景下增加复杂度
-
-
场景例子:
-
通过策略模式实现登录:账号密码登录、短信验证码登录、微信登录、UKey登录,登录方式的选择由请求参数决定
-
SpringBoot项目可以用@Conditional、@Profile注解实现bean的条件初始化
// 条件化配置策略 @Conditional(OnClassCondition.class) // 类路径条件 @Conditional(OnBeanCondition.class) // Bean 存在条件 @Conditional(OnPropertyCondition.class) // 配置属性条件 @Conditional(OnWebApplicationCondition.class) // Web 应用条件 // 不同环境的不同配置策略 @Profile("dev") // 开发环境策略 @Profile("test") // 测试环境策略 @Profile("prod") // 生产环境策略
-
-
-
观察者模式:
- 什么是观察者模式:观察者模式是发布-订阅模式的一种实现,用于定义对象之间一对多的依赖关系,当被观察者状态修改时,所有依赖该对象的观察者都会等到通知
- 三要素:被观察者、观察者、自动通知机制
- 观察者模式和发布-订阅模式的区别:
- 观察者模式:被观察者与观察者直接通信
- 发布-订阅模式:发布和订阅对象之间通过消息代理,间接通信
- 优缺点:
- 优点:
- 将被观察者和观察者解耦,被观察者状态修改时自动通知所有观察者
- 新增观察者无需修改被观察者代码
- 缺点:
- 增加代码复杂度:需要额外维护观察者列表和自动通知逻辑
- 性能消耗:随着观察者数量的增加,通知耗时增加
- 优点:
- 场景例子:
- Spring的事件机制就是观察者模式的实践
- 不引入mq的情况下,可以通过观察者模式解耦用户注册与消息通知、积分添加等业务
- 什么是观察者模式:观察者模式是发布-订阅模式的一种实现,用于定义对象之间一对多的依赖关系,当被观察者状态修改时,所有依赖该对象的观察者都会等到通知
-
装饰器模式:
-
什么是装饰器模式:通过对象的动态组合替代类继承,实现功能增强
-
优缺点:
- 优点:
- 功能灵活组合,避免类膨胀
- 不修改原有类的代码
- 能够在程序运行时动态加装饰器
- 缺点:
- 多层装饰增加代码复杂度
- 无法替代继承的全部场景,例如通用功能还是适合抽取父类,然后子类继承比较好
- 优点:
-
场景例子:
- Java中的各种IO流就是装饰器模式的经典应用
-
为什么装饰器模式可以避免类膨胀?
例子:基础类咖啡,如果通过继承扩张功能,就会产生拿铁、生椰拿铁、冰拿铁、黄油拿铁等n多类,变成排列组合问题;装饰器模式将加冰、加奶、加糖等操作封装成装饰器,可以动态选择拓展组合,避免类膨胀
-
-
代理模式:
- 什么是代理模式:通过引入一个代理对象来控制访问原有对象,代理对象作为中介将访问被代理对象的方法或者属性,同时可以在真正执行前后添加增强逻辑
- 优缺点:
- 优点:
- 灵活性:可以在方法执行前后添加增强逻辑
- 职责分离:代理模式将访问控制和实际业务处理分离
- 缺点:
- 性能消耗:代理对象处理速度会慢一点
- 优点:
- 场景例子:
- JDK动态代理和Cglib动态代理:前者基于接口实现(通过Proxy创建代理类),后者基于继承实现(通过Enhancer创建子类代理)
- AOP面向切面编程
-
建造者模式:
- 什么是建造者模式:将一个复杂对象构建过程拆分成多个步骤,像积木一样组装得到不同的实例
- 优缺点:
- 优点:
- 链式调用,分步构建,灵活可控
- 适用参数比较多的复杂对象
- 可以在build方法中对参数进行统一校验
- 缺点:
- 增加代码复杂度
- 性能比setter、构造方法初始化对象稍微慢一点
- 场景例子:
- StringBuilder
- Lombok提供@Builder注解快速让类支持建造者模式链式调用
- 适用对象参数很多的情况
- 优点: