面试过程中会让你介绍你项目或实习中使用的设计模式,你该如何说明
工厂模式
工厂模式是把对象创建的逻辑封装到一个工厂类里面,我们用工厂类来创建对象
为什么需要工厂类(优点):
- 可以集中管理对象的创建规则。例如要求这个对象必须经过xx校验,写到工厂类里好过每一次使用这个类的时候再检验一轮
- 提高代码可维护性和拓展性。工厂类可以通过接口或抽象类定义创建方法,允许在不修改现有代码的情况下添加新的产品类型
- 简化复杂对象创建逻辑,优化代码冗余。对象的创建过程可能涉及复杂的初始化步骤(如参数校验、依赖注入、资源配置等),这些逻辑分散在业务代码中会导致代码冗余和可读性下降
- 对象逻辑变动时,无需修改具体业务,只需修改工厂类。将对象创建逻辑封装在工厂类中,业务代码只需通过工厂获取对象,无需关心具体实现。即使后续对象的创建方式变化,只需修改工厂类,而不影响业务逻辑
缺点:
- 过度设计问题。在需求简单且变化不大的场景下使用工厂模式,可能导致 “设计过度”,增加维护成本
- 子类膨胀问题。工厂方法模式中,每新增一种产品就需要新增一个具体工厂类,导致子类数量过多,增加系统负担
什么业务场景使用工厂模式:
- 对象要进行大量的校验逻辑或创建逻辑非常复杂。例如Sqlsession需要url,账号,密码,数据库类型等
- 一个类有多个子类,且该类适用于多个不同的场景。例如这是一个创建游戏角色的工厂类,有射手,战士,刺客等
Spring:
Datasource,使用工厂模式创建不同类型的数据源,客户端只需依赖 DataSource
接口,无需关心具体实现
可通过配置动态切换数据源(如从 HikariCP 切换到 Druid)
策略模式
策略模式是策略接口来定义统一行为的,具体的策略细节由子类来实现
可以将策略理解成方法,也就是方法类
例如你可以定义一个队列Queue,Queue的基本方法是add()和remove(),这个就是队列的基本行为。其他的优先队列等他会有Queue的基本行为,然后再进行拓展。
为什么需要策略模式(优点):
- 当前业务有统一的基础方法
- 策略模式切换灵活,可在基类基础上应对不同的场景
- 避免大量的if-else,代码可读性更强
缺点:
- 策略过多时会增加维护成本
- 简单策略导致的资源浪费,策略细节必须由子类来实现,所以即使是很简单的实现类也要弄多一个子类
什么业务场景使用策略模式:
业务有共同的行为,例如飞书通知和钉钉通知这种IM通知,一般来说都会定义一个接口有notify()基本方法来进行通知,这是他们的基本方法,其他方法自己在子类进行自定义增强
Spring:
TransationalManager事务管理器有
getTransational(),commit(),rollback()三种方法
它的子实现类有JPA事务和JDBC事务
责任链模式
责任链模式是让我们的一个请求,沿着一个链条从前往后进行处理
将多个处理者连成一条链,并沿着这条链传递请求,直到有对象处理它为止
优点:
- 解耦,降低耦合度,只需将实现类提交到责任链中
- 责任链动态调整机制,无需修改原有的代码,只需要修改责任链就可以达到业务修改
- 适用于串行,层次化处理逻辑
缺点:
- 配置失败可能会导致请求漏处理风险
- 处理链过长导致的性能下降问题
- 引入过多的类和对象增加系统复杂度
- 循环引用问题
什么业务场景使用责任链模式:
- 大量的if-else复杂逻辑处理,串行处理
- 处理逻辑可以复用,并且可以组合链节点应对不同业务的场景
Spring:
Spring的过滤器filter和拦截器interceptor用到了责任链模式
先进入DispatcherServlet拦截器-preHandler请求先处理-postHandler处理视图渲染前逻辑-afterCompletion请求后处理
单例模式
保证一类对象只有一个实例,防止创建多个实例,导致资源占用,使用对象不一致等问题
为什么需要单例模式:
- 全局状态管理,系统中需要维护一个全局状态(如配置信息、计数器),确保不同模块访问的是同一状态。例如日志管理器,配置管理器,Spring默认是单例-RedisTemplate-KafkaTemplate
- 需要为多个模块提供统一的资源访问入口。数据库连接池,线程池
- 适用于可复用场景,避免重复创造类。创建实例需要消耗大量资源,单例可减少开销
- 保证多线程下的线程安全和状态同步。如全局计数器和序列化生成器保证并发安全
缺点:
- 违反单一职责和开闭职责,单例类既负责创建又承担业务逻辑,单例类通常难以扩展,若需修改行为,可能需要直接修改单例类代码
- 若单例创建未正确处理线程安全,可能导致多实例问题。如双重检查锁定未使用 volatile
- 内存泄漏问题。单例持有的资源若未正确释放,会导致内存泄漏
什么业务场景使用单例模式:
- 需要有一个全局统一的实例,而不是多实例。如日志管理器,数据库连接池,线程池
- 需要保证多线程下的线程安全问题。如全局计数器
Spring:
Spring的Bean默认是单例模式,如RedisTemplate和KafkaTemplate等