行为型模式

共 79 题
📑 题目列表 79 题
#
★★★

1. Command 模式在 CQRS 写模型中的工程价值,与直接调用 Service 方法相比解决了哪些核心痛点

请说明 Command 模式在 CQRS 写模型中的工程价值,以及与直接调用 Service 方法相比解决了哪些核心痛点?

  • Command 封装请求为对象
  • CQRS 写模型的命令化
  • 与直接调用 Service 的对比

Command 模式把"请求/操作"封装为对象,可与 CQRS 写模型结合:写入操作封装为 Command 对象,统一处理(校验、执行、日志、持久化)。相比直接调用 Service 方法,Command 模式解决的核心痛点:命令可排队、可持久化、可重放(审计/重试)、可解耦(调用方与执行方分离)、可统一前置后置处理(拦截、事务)。在 CQRS 中,命令是写入的载体,可支持命令总线、命令处理器、幂等与审计。

Command 把"做什么"对象化,提供可排队、可重放、可审计的能力,是 CQRS 写侧的理想载体。

#
★★★

2. Memento 模式在 Immutable + Event Sourcing 主导的现代架构中还有真实工程价值吗

在 Immutable + Event Sourcing 主导的现代架构中,Memento 模式还有真实工程价值吗?

  • Memento 的状态快照
  • Event Sourcing 的替代
  • 价值判断

Memento(备忘录)模式保存对象状态快照以支持撤销/恢复。在 Immutable + Event Sourcing 架构中,状态可通过重放事件恢复,Memento 的"快照"需求被 Event Sourcing 的"事件序列"部分替代。但 Memento 仍有价值:Event Sourcing 重放历史事件成本高,用快照(Memento 思想)加速恢复;事务/回滚、undo 场景仍需要状态快照。因此 Memento 在现代架构中仍有价值,但常与 Event Sourcing 的快照机制结合,而非取代。

Event Sourcing 用事件重放替代快照,但重放性能差,快照(Memento)作为加速与恢复手段仍必要。

#
★★★

3. Memento 模式在 Spring Cache 与 Redis 持久化快照中的应用与 TTL 失效协作

请说明 Memento 模式在 Spring Cache 与 Redis 持久化快照中的应用与 TTL 失效协作?

  • Memento 的快照存储
  • Spring Cache 与 Redis
  • TTL 失效

Memento 模式把状态快照保存起来,Spring Cache 配合 Redis 可把"快照"持久化到 Redis,实现跨请求、跨实例的状态/缓存复用。快照通过 Redis 的 TTL 设置失效时间,过期后自动删除,配合缓存更新保证数据新鲜。工程上:把需要保存的状态(如计算结果、对象快照)用 @Cacheable 存储到 Redis,设置适当 TTL 实现"快照 + 自动失效",避免内存堆积与脏数据。Memento 提供"快照"语义,Redis TTL 提供"失效"语义。

Memento 思想(保存可恢复状态)与缓存(Redis + TTL)结合,实现持久化快照与自动失效。

#
★★★

4. Observer 模式与发布订阅消息中间件(Kafka/RabbitMQ)

请说明 Observer 模式与发布订阅消息中间件(Kafka/RabbitMQ)的关系?

  • Observer 的同步观察
  • 中间件的异步发布订阅
  • 差异

Observer(观察者)模式是进程内的事件通知:被观察者维护观察者列表,状态变化时同步通知,观察者与被观察者强耦合(同进程)。发布订阅消息中间件(Kafka/RabbitMQ)是跨进程的异步通知:生产者发布事件到主题/队列,消费者订阅,解耦、可扩展、持久化。二者思想相似(事件驱动),但 Observer 同步、进程内、简单;消息中间件异步、跨进程、可靠。Observer 适合进程内简单通知,消息中间件适合分布式解耦。

Observer 是"进程内同步通知",消息中间件是"跨进程异步发布订阅"。按解耦与可靠性需求选择。

#
★★★

5. Observer 模式的事件发布是否仍需 Synchronized 保护事件源

Observer 模式的事件发布是否仍需 Synchronized 保护事件源?

  • 观察者列表的并发安全
  • 事件发布的安全性
  • 同步策略

是的,Observer 模式的事件发布需要考虑并发安全:被观察者的观察者列表如果被多个线程并发修改(注册/注销)或遍历通知,可能产生 ConcurrentModificationException 或数据不一致。因此对观察者列表的修改与遍历需要 synchronized 或使用 CopyOnWriteArrayList 等并发容器保护。事件源本身(状态变更)也需保证发布时的原子性。若事件发布是异步的,还需注意线程安全。

观察者列表的"遍历+修改"并发是风险点,需同步或并发容器。事件发布并发安全是工程细节。

#
★★★

6. Servlet Filter 与 WebFlux WebFilter 的责任链在线程模型和异步上下文传播上有何差异

请说明 Servlet Filter 与 WebFlux WebFilter 的责任链在线程模型和异步上下文传播上的差异?

  • Servlet Filter 的阻塞线程模型
  • WebFlux WebFilter 的响应式
  • 异步上下文传播

Servlet Filter 基于阻塞 I/O 模型,每个请求占用一个线程,责任链在请求线程上同步执行,上下文(如 MDC、SecurityContext)随线程传播;WebFlux WebFilter 基于响应式(非阻塞)模型,请求在事件循环上处理,责任链通过 WebFilterChain 异步传递,可能跨线程切换,上下文传播需要响应式上下文(如 Reactor Context)而非线程本地。差异核心:线程模型(阻塞 vs 非阻塞)与上下文传播方式(线程本地 vs 响应式上下文)。

线程模型决定上下文传播方式。WebFlux 非阻塞跨线程,需用 Reactor Context 传播而非 ThreadLocal。

#
★★★

7. Spring 事务事件监听器选择 BEFORE_COMMIT 或 AFTER_COMMIT 时,失败语义和补偿方式有何区别

Spring 事务事件监听器选择 BEFORE_COMMIT 或 AFTER_COMMIT 时,失败语义和补偿方式有何区别?

  • BEFORE_COMMIT 在提交前执行
  • AFTER_COMMIT 在提交后执行
  • 失败语义与补偿

@TransactionalEventListener 的 phase 决定执行时机:BEFORE_COMMIT 在事务提交前、提交事务内执行,监听器失败会导致事务回滚(与业务同事务);AFTER_COMMIT 在事务提交后执行,监听器已脱离事务,失败不影响已提交的事务,但需自行补偿(如重试、记录失败)。选择依据:若监听器动作需要与业务一致(失败则回滚)用 BEFORE_COMMIT;若需在提交后执行(如发消息、通知)用 AFTER_COMMIT,并处理失败补偿。

时机决定事务边界:BEFORE 与事务一致,AFTER 脱离事务需补偿。语义选择是关键。

#
★★★

8. Spring 框架中 Strategy 模式与 @Conditional 在 Spring Boot 4.0 自动装配中的角色与协作机制

请说明 Strategy 模式与 @Conditional 在 Spring Boot 4.0 自动装配中的角色与协作机制?

  • Strategy 的策略选择
  • @Conditional 的条件装配
  • 自动装配的协作

在 Spring Boot 自动装配中,Strategy 模式用于"多个策略实现按条件选择",@Conditional 用于"条件装配"——根据条件(类存在、属性、Bean 存在)决定是否注册某个 Bean/策略。二者协作:自动装配通过 @Conditional 判断环境条件,按需注册对应的 Strategy 实现,符合条件时装配特定策略。例如自动配置类根据是否引入某个库、是否配置属性,用 @Conditional 决定注册哪个策略 Bean,交给容器按策略使用,实现"条件驱动、按需选择"。

@Conditional 决定"配不配",Strategy 决定"用哪个"。自动装配用条件控制策略注入,是 Spring Boot 扩展性的核心。

#
★★★

9. Visitor 模式在 Java 编译器(javax.lang.model)

请说明 Visitor 模式在 Java 编译器(javax.lang.model)中的应用?

  • Visitor 的访问者
  • javax.lang.model 的元素访问
  • 分离操作与数据结构

Java 编译器工具 javax.lang.model 用 Visitor 模式遍历注解处理中的元素(Element)与类型(Type):ElementVisitor 提供 visitTypevisitMethodvisitField 等方法,注解处理器可访问不同的元素类型而无需 if-else 判断。Visitor 把"数据结构(元素树)"与"操作(访问逻辑)"分离,新增操作只需新增 Visitor 实现,无需修改元素类。这是 Visitor 在编译器/注解处理中的典型应用。

Visitor 分离"遍历数据结构"与"定义操作",javax.lang.model 用它访问元素树,新增操作易扩展。

#
★★★

10. 使用 Null Object 模式替代 if (obj != null) 在 Spring Bean 注入中的可读性与测试收益

使用 Null Object 模式替代 if (obj != null) 在 Spring Bean 注入中的可读性与测试收益是什么?

  • Null Object 消除判空
  • Bean 注入的可读性
  • 测试收益

在 Spring Bean 注入中,若某个依赖可能为空(可选依赖),常写 if (obj != null) 判空。用 Null Object 模式:提供一个"无操作"的默认实现作为 Bean,注入时总有一个非空对象,消除判空,代码更可读。测试收益:测试时注入 Null Object 或桩实现,无需处理空值分支,测试更简洁;Null Object 的默认行为可预测,便于构造测试场景。可读性提升源于"无判空分支",测试收益源于"状态可预测"。

Null Object 用非空默认对象消除判空,提升可读性;测试注入可预测的默认行为,简化测试。

#
★★★

11. 发执行命令时,接收者线程安全与命令对象不可变分别解决什么问题,如何测试

发执行命令时,接收者线程安全与命令对象不可变分别解决什么问题,如何测试?

  • 命令对象不可变
  • 接收者线程安全
  • 测试方法

命令对象不可变解决"命令被并发队列/处理器共享时的数据竞争"——不可变命令可安全共享、可重放、无外部副作用;接收者线程安全解决"接收者被多个线程并发调用时的状态一致"——接收者保护共享状态。测试:不可变命令用并发测试(多线程提交同一命令验证一致);接收者用并发压力测试验证状态不变量;命令执行测试验证幂等与结果。二者配合保证命令模式在并发下的正确性。

不可变命令解决"共享安全",线程安全接收者解决"状态一致"。并发测试验证这两点。

#
★★★

12. 命令模式在 FutureTask 的应用

请说明命令模式在 FutureTask 中的应用?

  • FutureTask 封装可执行任务
  • 命令对象化
  • 与异步执行

FutureTask 是命令模式的应用:它把可执行的任务(Callable)封装为对象,可提交给线程池执行,并返回 Future 获取结果。FutureTask 本身是"命令"(可执行),通过 run() 执行,同时管理状态(New/Completed/Running)与结果。它把"任务"对象化,支持排队、异步执行、结果获取、取消。命令模式把"操作"封装为对象,FutureTask 把"任务"封装为可执行对象并支持异步。

FutureTask 把"任务"封装为命令对象,支持线程池执行与结果获取,是命令模式在并发中的应用。

#
★★

13. 命令模式用于消息重试或撤销时,命令对象应如何携带幂等键、授权信息和补偿状态以避免重复副作用

命令模式用于消息重试或撤销时,命令对象应如何携带幂等键、授权信息和补偿状态以避免重复副作用?

  • 幂等键
  • 授权信息
  • 补偿状态

命令用于消息重试时,命令对象应携带:幂等键(唯一标识命令,重试时据此去重,避免重复执行副作用)、授权信息(执行者身份、权限,保证重试时仍合法)、补偿状态(记录已执行步骤,用于失败时补偿或撤销)。这样重试不会重复副作用(幂等)、权限验证一致(授权)、失败可恢复(补偿)。命令对象是"携带上下文"的载体,把这些信息与其操作绑定。

命令对象携带幂等键、授权、补偿状态,是"可重试、可撤销、可审计"命令的关键。

#
★★

14. 命令模式(Command)在 CQRS 与 Spring CommandLineRunner 的应用差异

请说明命令模式(Command)在 CQRS 与 Spring CommandLineRunner 的应用差异?

  • CQRS 的命令写模型
  • CommandLineRunner 的命令启动
  • 差异

在 CQRS 中,命令模式用于写模型:命令封装写入操作,通过命令总线路由到命令处理器,支持排队、审计、重放。在 Spring CommandLineRunner 中,命令模式用于"启动时执行":CommandLineRunner 在应用启动后执行一次性任务,可理解为"启动命令",但它是简单的启动钩子,不具备 CQRS 命令的完整语义(无命令总线、无队列)。差异:CQRS 命令是业务写入的载体,CommandLineRunner 是启动任务的钩子,二者用途不同。

CQRS 命令是"业务写入对象",CommandLineRunner 是"启动钩子"。概念不同,应用层级不同。

#
★★

15. 回调式模板需要管理连接或锁时,如何确保用户函数异常也不会跳过统一资源清理

回调式模板需要管理连接或锁时,如何确保用户函数异常也不会跳过统一资源清理?

  • 模板方法管理资源
  • 异常下的资源清理
  • try/finally

回调式模板(如 JdbcTemplate、TransactionTemplate)把资源管理封装在模板中,用户函数作为回调传入。为确保用户函数异常也不跳过资源清理,模板应用 try/finally(或 try-with-resources)包裹回调和资源管理:无论回调是否抛异常,finally 中都会释放连接、关闭流、释放锁。这样资源清理由模板统一保证,用户只需提供业务回调,无需关心资源释放。这是模板方法"资源托底"的设计。

try/finally 保证"异常也清理"。模板把资源生命周期集中管理,回调只负责业务,异常安全。

#
★★

16. 如何说服团队在合适场景引入策略/责任链等行为型模式而非堆 if-else

如何说服团队在合适场景引入策略/责任链等行为型模式而非堆 if-else?

  • if-else 复杂分支的坏处
  • 行为型模式的收益
  • 说服与落地

说服团队引入行为型模式而非堆 if-else,可从以下角度:if-else 会随分支增长变得难以维护、难以测试、违反 OCP(新增分支需改代码);策略模式把分支转化为策略对象,可扩展、可测试、可复用;责任链把多级处理解耦为链。落地方式:用具体案例展示 if-else 的痛点与模式后的可读性、可测试性提升;明确"分支复杂度达到阈值才引入",避免过度设计。用收益与场景数据说服,而非空谈模式。

说服的关键是"用具体痛点与收益"而非教条。模式引入要与复杂度匹配,避免形式主义。

#
★★

17. 模板方法在 AbstractApplicationContext.refresh 的应用

请说明模板方法在 AbstractApplicationContext.refresh 中的应用?

  • refresh 的算法骨架
  • 钩子方法
  • 中心的子类扩展

AbstractApplicationContext.refresh() 是模板方法模式的典型应用:它定义了 Spring 容器刷新(创建 BeanFactory、注册 BeanPostProcessor、初始化单例、发布事件等)的算法骨架,步骤顺序固定(obtainFreshBeanFactory、prepareBeanFactory、postProcessBeanFactory、finishBeanFactoryInitialization 等),其中部分步骤是钩子方法(postProcessBeanFactory 等),子类(如 AnnotationConfigApplicationContext)可覆盖钩子定制行为。模板方法固定骨架、子类扩展变化点。

refresh 是"算法骨架 + 钩子"的模板方法,子类通过覆盖钩子定制容器,核心流程不可变。

#
★★

18. 状态机如何显式阻止非法迁移,并在持久化并发、重复事件和失败重放时保持状态与副作用一致

状态机如何显式阻止非法迁移,并在持久化并发、重复事件和失败重放时保持状态与副作用一致?

  • 非法迁移的显式阻止
  • 持久化并发
  • 重复事件与重放一致性

状态机显式阻止非法迁移:定义合法的迁移表(state + event → targetState),非法迁移抛异常或拒绝,避免进入未定义状态。在持久化并发下,状态迁移应原子更新(乐观锁 version 或 CAS),防止并发把状态覆盖;重复事件用幂等(事件 ID 去重)保证副作用只执行一次;失败重放时,状态迁移与副作用应一致(先判状态再副作用,或副作用可重放)。三者保证状态机在分布式下的正确性。

状态机正确性靠"合法迁移约束 + 原子更新 + 幂等 + 一致重放"。并发与重放是分布式状态机的难点。

#
★★

19. 策略链同时包含重试、熔断和超时时,模式组合顺序如何改变总时限与错误可见性

策略链同时包含重试、熔断和超时时,模式组合顺序如何改变总时限与错误可见性?

  • 策略链顺序
  • 总时限与错误可见性
  • 组合

当重试、熔断、超时组合成策略链时,顺序决定总时限与错误可见性:若"超时在外层、重试在内层",一次调用里重试共享外层超时,总时限受控但重试次数被限制;若"重试在外层、超时在内层",每次重试独立超时,总时限可能累加(重试次数 × 单次超时)。错误可见性也不同:外层捕获的异常类型影响熔断判断。合理顺序:超时包裹单次调用,重试在超时之上,熔断在最外,保证总时限与故障隔离。

组合顺序改变"超时累计"与"异常可见性"。需按"单次超时 < 重试总时限 < 熔断"的层次设计。

#
★★

20. 行为型模式在多线程上下文中的内存可见性与可重入性问题有哪些典型踩坑案例

行为型模式在多线程上下文中的内存可见性与可重入性问题有哪些典型踩坑案例?

  • 内存可见性
  • 可重入性
  • 典型踩坑

行为型模式在多线程下的典型踩坑:内存可见性——单例/观察者列表/状态若不加 volatile/同步,多线程可能读到旧值(如双重检查锁缺 volatile、观察者状态不同步);可重入性——被观察者通知触发观察者再次注册/修改列表导致 ConcurrentModificationException,或状态机的回调中触发状态变更导致重入;命令/状态共享。这些坑源于"共享状态未同步"与"回调重入"。解决:用 volatile/并发容器/同步保证可见性,用拷贝副本/CopyOnWrite 避免遍历中修改,用重入保护(标志位)避免递归重入。

共享可变状态 + 回调嵌套是行为型模式并发坑的根源。可见性用 volatile,重入用防护。

#
★★

21. 访问者模式在编译器、规则引擎等 AST 场景外的工程价值是否真实存在,哪些被滥用的反模式值得警惕

访问者模式在编译器、规则引擎等 AST 场景外的工程价值是否真实存在,哪些被滥用的反模式值得警惕?

  • 访问者的适用场景
  • AST 外的价值
  • 反模式

访问者模式在 AST、编译器、规则引擎等"数据结构稳定、操作多变"场景外,工程价值有限且易被滥用:它适合"元素类型固定、操作频繁新增"的场景,把操作从数据结构中抽离。若元素类型频繁变化,访问者会因新增元素需改所有访问者而脆弱。反模式:把访问者用于简单对象(成本大于收益)、元素类型易变(违反"数据稳定"前提)、用访问者规避类型判断导致代码复杂。滥用访问者会引入不必要的 visitor 爆炸与样板。

访问者的价值前提是"数据结构稳定、操作多变"。若元素易变,访问者反而增加维护成本。

#
★★

22. 责任链中的节点既可能短路也可能继续传播,如何定义顺序、异常处理和幂等约束,保证链行为可预测

责任链中的节点既可能短路也可能继续传播,如何定义顺序、异常处理和幂等约束,保证链行为可预测?

  • 链的顺序定义
  • 异常处理
  • 幂等约束

责任链保证可预测需明确:顺序(节点按确定的优先级/顺序排列,文档化);短路语义(每个节点明确"处理并终止"或"处理后继续");异常处理(节点异常是终止链还是继续,需统一策略);幂等约束(节点处理需幂等,重复执行不产生重复副作用)。把这些规则显式定义,链的行为才可预测。用配置或声明顺序,异常有明确归属,幂等保证重试安全。

链的可预测性来自"顺序、短路、异常、幂等"四者的显式约定。模糊的短路与异常会让链不可控。

#
★★

23. Iterator 模式在 Java Stream 与 Spliterator 并行流框架下的延迟求值与短路实现细节

请说明 Iterator 模式在 Java Stream 与 Spliterator 并行流框架下的延迟求值与短路实现细节?

  • 迭代器模式
  • 流的延迟求值
  • Spliterator 并行

Java Stream 基于迭代器思想,但增强为 Spliterator(可分割迭代器):tryAdvance 逐个取元素实现延迟求值(元素按需计算),trySplit 分割数据支持并行流。流的 filter/map 是中间操作(延迟),collect 是终端操作(触发求值)。短路操作(如 findFirstlimit)只消费部分元素即终止,依赖 Spliterator 的按需推进。Iterator 的"逐个访问"与 Spliterator 的"分割并行"结合,支持延迟求值与短路。

延迟求值靠"按需迭代",短路靠"提前终止迭代",并行靠"分割迭代"。Spliterator 是三者的基础。

#
★★

24. 枚举策略(Enum Strategy)与 Map 策略注册表在消除分支上的取舍

请说明枚举策略(Enum Strategy)与 Map 策略注册表在消除分支上的取舍?

  • 枚举策略
  • Map 策略注册表
  • 取舍

枚举策略(Enum Strategy)用枚举实现策略,每个枚举常量实现策略方法,消除分支且类型安全、可序列化;Map 策略注册表用 Map 把策略标识映射到策略对象,支持动态注册、扩展新策略无需改枚举。取舍:枚举策略简单、类型安全、适合固定且数量少的策略;Map 注册表灵活、可动态扩展、适合策略数量多或需运行时注册的场景。枚举策略扩展需改枚举(违反 OCP),Map 注册表更开放。

枚举策略是"静态策略",Map 注册表是"动态策略"。按策略是否固定与扩展需求选择。

#
★★

25. Mediator 模式在 Spring Cloud 中的 Spring Cloud Bus 与 Spring Cloud Stream 消息协调器角色

请说明 Mediator 模式在 Spring Cloud 中的 Spring Cloud Bus 与 Spring Cloud Stream 的消息协调器角色?

  • Mediator 的集中协调
  • Spring Cloud Bus/Stream
  • 消息协调

Mediator(中介者)模式集中协调多个对象的交互,避免网状依赖。Spring Cloud Bus 是 Mediator 的体现:它作为消息协调器,把服务间的配置刷新、事件广播通过消息总线(如 Kafka、RabbitMQ)集中分发给各实例,避免各服务直接互相调用。Spring Cloud Stream 作为消息中间件抽象,协调生产/消费的绑定。二者充当"协调中心",让服务间通过消息协同而非直接耦合,降低网状依赖。

中介者把"多对多"的交互集中为"与中心交互"。Bus/Stream 作为消息协调器降低服务网状耦合。

#
★★

26. Null Object 模式在 Kotlin(?类型系统)

请说明 Null Object 模式在 Kotlin(?类型系统)中的体现?

  • Kotlin 的可空类型
  • 空安全
  • Null Object 的替代

Kotlin 的可空类型(?)与空安全机制(?.?:!!)在编译期强制处理空值,替代了 Java 的空对象模式的一部分需求:Kotlin 用类型系统表达"可空",用安全调用与 Elvis 处理空值,无需单独的 Null Object 类。但 Null Object 模式仍有价值:当需要"默认行为"(如空集合、默认策略)时,用非空对象表达默认语义比判空更清晰。Kotlin 的类型系统减少判空,Null Object 提供默认行为,二者互补。

Kotlin 空安全用类型系统消除判空,Null Object 提供"默认行为"语义,互补而非替代。

#
★★

27. Observer 模式在 Spring 的 ApplicationEventPublisher 与 ApplicationListener 中的实现原理

请说明 Observer 模式在 Spring 的 ApplicationEventPublisher 与 ApplicationListener 中的实现原理?

  • ApplicationEventPublisher 发布事件
  • ApplicationListener 监听
  • 观察者机制

Spring 的事件机制(ApplicationEventPublisher + ApplicationListener)是 Observer 模式的实现:ApplicationEventPublisher.publishEvent() 发布事件(被观察者),ApplicationListener@EventListener 监听事件(观察者),Spring 的 ApplicationEventMulticaster 负责把事件分发给所有监听器。发布者与监听者解耦,发布者不关心谁监听。监听器默认同步执行,可配置异步(@Async)。这是观察者模式在 Spring 容器的工程化。

Spring 事件用 multicaster 分发,实现"发布-监听"解耦,是观察者模式的容器实现。

#
★★

28. Specification + Repository 模式如何支撑领域规则的单元测试独立演化

请说明 Specification + Repository 模式如何支撑领域规则的单元测试独立演化?

  • Specification 封装查询规则
  • Repository 抽象数据访问
  • 独立测试

Specification 模式把查询规则封装为可复用的谓词对象,Repository 抽象数据访问接口。二者结合:领域规则(Specification)独立于数据访问(Repository),可单独单元测试(Specification 是纯逻辑,不依赖数据库);Repository 可 mock/替换,测试领域逻辑时用内存实现。事件上,Specification 的规则演化不影响 Repository,Repository 的实现变化不影响规则,二者独立演化,测试聚焦于各自的逻辑。

Specification 是"查询规则",Repository 是"数据访问",分离后各自可独立测试与演化。

#
★★

29. Spring ApplicationEvent 的观察者发布默认可能同步执行,监听器变慢或失败时如何设计异步化、隔离与重试

Spring ApplicationEvent 的观察者发布默认可能同步执行,监听器变慢或失败时如何设计异步化、隔离与重试?

  • 同步发布的阻塞
  • 异步化
  • 隔离与重试

Spring 事件默认同步发布,监听器变慢会阻塞发布者、失败会传播错误。设计上:异步化用 @Async 监听器或配置 ApplicationEventMulticaster 的 TaskExecutor,让监听器在独立线程执行,避免阻塞发布者;隔离用独立的线程池/异步队列,避免一个监听器拖垮其他;重试用 RetryTemplate、消息队列或记录失败事件重投,保证监听失败不丢失。异步化需注意事务边界与上下文传播。

同步发布阻塞是风险,异步化、隔离、重试是"监听器可靠"的三件套。异步需注意事务与上下文。

#
★★

30. 中介者在 ScheduledExecutorService 的协调

请说明中介者(Mediator)在 ScheduledExecutorService 的协调?

  • 中介者的协调
  • 定时任务调度
  • 统一调度

ScheduledExecutorService 作为"定时调度器"可充当中介者:多个需要定时执行的任务统一提交给它,由它集中协调任务的调度(周期、延迟、并发),避免各任务各自管理线程。中介者模式集中了"任务与调度"的交互。工程上,ScheduledExecutorService 作为统一的调度中介,管理任务的注册、执行、异常处理,隔离任务间的相互影响。

调度器作为中介者集中协调任务调度,避免每个任务各自管理线程。统一调度提升可控性。

#
★★

31. 中介者模式(Mediator)与聊天室

请说明中介者模式(Mediator)与聊天室的关系?

  • 中介者集中协调
  • 聊天室的消息转发
  • 避免网状依赖

聊天室是中介者模式的经典案例:用户(Colleague)不直接互相通信,而是通过聊天室(Mediator)转发消息,避免用户间点对点的网状依赖。聊天室集中管理消息的广播、用户注册。若没有中介者,N 个用户需 N-1 个连接,中介者让用户只与中介者交互,降低耦合、便于集中控制(如过滤、审计)。中介者模式适合"多对象复杂交互"的场景。

中介者把"多对多"交互集中为"与中心交互",聊天室是典型,降低网状依赖。

#
★★

32. 中介者模式(Mediator)在 MediatR 模式与 Spring 事件总线的工程取舍

请说明中介者模式(Mediator)在 MediatR 模式与 Spring 事件总线的工程取舍?

  • MediatR 的命令中介
  • Spring 事件总线
  • 取舍

MediatR 是 .NET 的命令/事件中介者模式,把请求路由到处理器,集中处理命令、查询、通知;Spring 事件总线(ApplicationEventPublisher)也充当中介,发布事件给监听器。二者取舍:MediatR 强调"命令/查询分离"与处理器路由,适合 CQRS 与命令式架构;Spring 事件总线更通用、集成在 Spring 生态,适合进程内事件解耦。选择取决于是否需要命令路由、CQRS 语义、以及是否已用 Spring 生态。

二者都是中介者,但 MediatR 偏命令/查询路由,Spring 事件总线偏事件解耦。按需求选择。

#
★★

33. 中介者集中协调组件能降低网状依赖,但中介者膨胀后应按哪些业务协作边界拆分

中介者集中协调组件能降低网状依赖,但中介者膨胀后应按哪些业务协作边界拆分?

  • 中介者膨胀
  • 业务协作边界
  • 拆分

中介者集中协调能降低网状依赖,但所有交互都汇入一个中介者会导致其膨胀(职责过多、难维护)。拆分应按业务协作边界:把不同业务域的协调拆成多个中介者,每个中介者只负责一个业务域的协作;按"协作关系"(谁与谁协作)分组,而非把所有交互塞进一个中介者。拆分后每个中介者职责单一、可独立演化,符合 SRP。关键是根据业务边界与协作内聚性确定中介者的粒度。

中介者膨胀是"职责过多",按业务协作边界拆分多个中介者,职责单一、可维护。

#
★★

34. 从 Java 内置 Observer 退化到 EventListener 的过程中哪些语义被牺牲了

从 Java 内置 Observer 退化到 EventListener 的过程中哪些语义被牺牲了?

  • Observer 的语义
  • EventListener 的演进
  • 语义变化

Java 内置的 Observer/Observable 提供"被观察者维护观察者列表、状态变化通知"的通用语义,但已弃用。演进到 EventListener(如 ActionListener)等,更面向特定事件类型,语义上"通用观察者"被牺牲:EventListener 不再由 Observable 统一管理,而是由事件源手动注册;通知的通用 update() 被具体事件方法替代。牺牲的是"通用、自动地通知所有观察者"的抽象,换来的是更明确、类型安全、面向具体事件的事件处理。Observer 的通用通知语义被更细粒度的事件模型取代。

从 Observer 到 EventListener 是从"通用可观察"到"具体事件回调"的演进,牺牲通用性换取类型安全与明确性。

#
★★

35. 传统的 Mediator 模式如何被消息路由替代

传统的中介者模式(Mediator)如何被消息路由替代?

  • 中介者的集中协调
  • 消息路由
  • 替代

传统中介者集中协调对象交互,在分布式架构中,这种集中协调可被消息路由(消息中间件、事件总线)替代:对象通过发送消息到中间件,由路由(主题、队列)分发给相应消费者,替代"中介者直接转发"。消息路由把"中介者"从进程内的协调对象,演化为分布式的消息协调层,解耦更强、可扩展、可持久化。中介者的"集中转发"职责由消息路由承担,对象间通过消息解耦。

消息路由是中介者的"分布式形态",把进程内协调升级为消息驱动的解耦。

#
★★

36. 传统面向对象的行为型模式是否仍必要,哪些可被高阶函数直接替代

传统面向对象的行为型模式是否仍必要,哪些可被高阶函数直接替代?

  • 行为型模式的必要性
  • 高阶函数替代
  • 取舍

传统行为型模式在 Java 8+ 函数式特性下,部分可被高阶函数替代:策略模式可用 Function/Predicate/Lambda 替代(策略即函数);命令模式可用 Runnable/Supplier 等函数式接口替代;模板方法的钩子可用函数参数替代;观察者可用回调/流式函数替代。但模式仍必要:当逻辑复杂、需要状态、可读性、结构表达时,面向对象的模式仍清晰;高阶函数适合"单函数抽象",复杂协作仍需模式。取舍:简单行为用函数,复杂协作用模式。

高阶函数替代"单函数"的模式(策略、命令、观察者),复杂结构仍用模式。按复杂度权衡。

#
★★

37. 使用 Specification 模式(Spring Data JPA Specification)

请说明使用 Specification 模式(Spring Data JPA Specification)的实践?

  • Specification 的谓词封装
  • 动态查询
  • 组合

Spring Data JPA 的 Specification 把查询条件封装为可组合的谓词,Specification.where(...).and(...) 可动态组合查询条件,避免方法暴增。它把"查询规则"与"查询执行"分离,支持动态查询(按参数组合条件)、可复用(组合多个 Specification)、类型安全。配合 JpaSpecificationExecutor 执行。Specification 让查询条件可复用、可组合,适合动态查询场景。

Specification 封装谓词、支持逻辑组合,是动态查询与复用条件的理想方式。

#
★★

38. 使用 Spring 注入 Map<String, Strategy> 构建策略注册表时,如何避免键冲突、缺省实现和条件装配导致的隐患

使用 Spring 注入 Map<String, Strategy> 构建策略注册表时,如何避免键冲突、缺省实现和条件装配导致的隐患?

  • Map<String, Strategy> 注入
  • 键冲突
  • 缺省实现与条件装配

Map<String, Strategy> 注入策略注册表时,键通常是 Bean 名,潜在隐患:键冲突(多个 Bean 同名或命名不一致导致覆盖)、缺省实现(无匹配策略时抛异常)、条件装配(@Conditional 导致某些策略缺失)。解决:显式定义键(如用 @Qualifier 或自定义注解指定键名,避免 Bean 自动命名冲突);提供缺省实现(兜底策略,未匹配时调用);条件装配需明确哪些策略存在,避免注入时缺失。用 Map<String, Strategy> 的遍历与 getOrDefault 处理缺省。

策略注册表的风险在"键冲突、缺省缺失、条件装配"。显式键、兜底实现、清晰条件可规避。

#
★★

39. 命令模式支持撤销时,逆操作与状态快照各有什么限制,外部不可逆动作应如何补偿

命令模式支持撤销时,逆操作与状态快照各有什么限制,外部不可逆动作应如何补偿?

  • 逆操作的限制
  • 状态快照的限制
  • 外部不可逆动作补偿

命令撤销有两种方式:逆操作(undo 执行相反操作)与状态快照(保存状态用于恢复)。逆操作限制:并非所有操作都有简单逆操作(如发邮件、扣款),逆操作可能依赖顺序;状态快照限制:保存完整状态开销大,且外部副作用无法通过快照撤销。外部不可逆动作(消息发送、外部 API 调用)应通过补偿机制:记录操作日志,撤销时执行补偿操作(如退款、取消订单),或使用 Saga/补偿事务。补偿需保证幂等。

逆操作与快照只能处理"内部状态",外部副作用需补偿。不可逆动作的撤销是补偿事务。

#
★★

40. 命令模式(Command)与 Runnable/Callable

请说明命令模式(Command)与 Runnable/Callable 的关系?

  • 命令封装操作
  • Runnable/Callable 封装任务
  • 关系

命令模式把"操作"封装为对象,Runnable/Callable 是 Java 内置的"任务"抽象,本质上是命令模式的函数式体现:Runnable.run() 执行任务,Callable.call() 执行并返回结果。二者关系:Runnable/Callable 是命令对象的简化形式(把命令封装为可执行对象),可用于线程池、异步执行。命令模式比 Runnable 更完整(可携带参数、支持撤销、有明确接收者),但 Runnable/Callable 是 Java 中命令思想的常用实现。

Runnable/Callable 是命令模式的"可执行"简化,用于任务执行。完整命令模式含接收者、撤销等。

#
★★

41. 哪些重构动作可以同时保留扩展性与可读性

请说明哪些重构动作可以同时保留扩展性与可读性?

  • 重构方向
  • 扩展性与可读性
  • 兼顾

同时保留扩展性与可读性的重构动作包括:提取方法/类(让职责清晰、可复用);引入接口/抽象(面向抽象,扩展点);用策略/模板替换大型 if-else(分支变策略,可扩展且可读);用枚举/常量替代魔数(可读);把条件逻辑提取为谓词/Specification(可读且可组合)。这些重构让代码"可读"(职责清晰)且"可扩展"(抽象点),避免过度设计破坏可读性(如为不存在的扩展加抽象)。

兼顾扩展与可读的关键是"按真实变化点抽象"与"清晰命名职责"。避免为臆测扩展加抽象。

#
★★

42. 备忘录保存完整快照与增量事件各有什么空间和恢复成本,敏感字段又应如何保护

备忘录保存完整快照与增量事件各有什么空间和恢复成本,敏感字段又应如何保护?

  • 完整快照的空间与恢复
  • 增量事件
  • 敏感字段保护

完整快照占用空间大(保存全部状态),但恢复快(直接还原);增量事件占用空间小(只记录变化),但恢复需重放所有事件(成本随事件增长)。取舍:完整快照适合状态小、恢复频繁;增量事件适合状态大、变化多。敏感字段保护:保存快照时避免明文存储敏感数据(密码、令牌),用脱敏、加密、或只保存引用;备忘录对象应限制访问(封装),防泄漏。快照与增量按状态与恢复需求权衡,敏感字段需加密/脱敏。

完整快照空间大恢复快,增量空间小恢复慢。敏感字段需脱敏加密,快照访问需受限。

#
★★

43. 备忘录模式(Memento)与状态快照

请说明备忘录模式(Memento)与状态快照的关系?

  • Memento 的状态快照
  • 撤销/恢复
  • 封装

备忘录模式(Memento)的核心是"保存状态快照",用于撤销/恢复:Originator 保存状态到 Memento(快照),Caretaker 管理 Memento,需要时恢复。Memento 封装了状态,外部不能直接访问内部细节。状态快照是 Memento 的实现基础,Memento 提供"保存与恢复"的语义。适用场景:撤销操作、版本恢复、缓存快照。Memento 的封装性保证状态不被外部篡改。

Memento 是"状态快照"的模式化,保存与恢复封装。快照是内容,Memento 是封装。

#
★★

44. 如何为团队建立行为型模式的评审清单与反模式案例库,使模式引入有据可循

如何为团队建立行为型模式的评审清单与反模式案例库,使模式引入有据可循?

  • 评审清单
  • 反模式案例库
  • 模式引入依据

建立模式引入的依据:评审清单包含"模式是否解决真实问题、复杂度是否匹配、是否违反简单性原则、是否有更简单的替代(函数、if-else)、是否可测试"等条目,评审时按清单评估;反模式案例库记录"滥用模式的案例"(如过度策略、为用而用、访问者滥用),供团队参考避免。通过这些工具,模式引入有据可循,避免形式主义与过度设计。清单与案例库应随团队经验持续更新。

评审清单"防滥用",案例库"记教训",让模式引入基于问题与证据而非教条。

#
★★

45. 如何在小型项目中为可演进性预留抽象空间

如何在小型项目中为可演进性预留抽象空间?

  • 小型项目的抽象
  • 预留扩展点
  • 避免过度设计

小型项目为可演进性预留抽象空间,应在"真实变化点"处预留,而非全局抽象:在业务边界定义接口(如存储、外部服务),保持核心逻辑依赖抽象;用配置驱动(如策略注册)而非硬编码;保留清晰的模块边界。但要避免过度设计——不为臆测的扩展加抽象。预留空间的原则是"在可能变化处留接口,在稳定处直接实现",让架构可演进而不牺牲简洁。

预留抽象在"真实变化点",核心是"接口隔离变化 + 避免过度抽象"。小型项目宜精不宜繁。

#
★★

46. 如何用 Chain of Responsibility 模式优雅地实现多阶段(参数校验 → 鉴权 → 配额 → 业务)

如何用责任链(Chain of Responsibility)模式优雅地实现多阶段(参数校验 → 鉴权 → 配额 → 业务)?

  • 责任链的多阶段
  • 顺序与短路
  • 解耦

用责任链实现"参数校验 → 鉴权 → 配额 → 业务"的多阶段处理:把每个阶段封装为链节点,按顺序串联,每个节点处理自己的职责,通过则传给下一个,失败则短路(返回错误)。这样各阶段解耦、可插拔、顺序可配、可复用。责任链让"前置校验"与"业务处理"分离,新增阶段只需增加节点,符合 OCP。关键是将阶段顺序与短路语义定义清楚。

责任链把多阶段处理解耦为链节点,顺序串联、失败短路,可扩展、可维护。

#
★★

47. 如何选择与组合以避免双重拦截

如何选择与组合拦截机制以避免双重拦截?

  • 多层拦截机制
  • 双重拦截
  • 组合

多层拦截机制(Servlet Filter、Spring Interceptor、AOP、责任链)并存时,可能发生双重拦截(同一逻辑被多层执行)。避免方法:明确各层职责边界(Filter 管粗粒度、Interceptor 管 Controller 层、AOP 管业务方法),让同一关注点只在一层实现;用开关/配置控制是否启用各层;组合时避免嵌套重复。设计时先画清各层拦截范围,避免职责重叠导致双重执行。

双重拦截源于"职责重叠"。明确各层边界、单一职责归属,可避免重复执行。

#
★★

48. 如何避免过度设计并以业务复杂度驱动模式引入节奏

如何避免过度设计并以业务复杂度驱动模式引入节奏?

  • 避免过度设计
  • 业务复杂度驱动
  • 引入节奏

避免过度设计并以业务复杂度驱动模式引入:先实现简单方案,当业务复杂度(分支增多、条件复杂、需求变化)达到阈值时再引入模式,而非一开始就套用模式。用"复杂度度量"(分支数、职责数、变化频率)判断何时重构引入策略/责任链等。引入节奏:随复杂度渐进引入,每引入一个模式验证其收益,避免"为未来设计"。核心是"复杂度驱动、渐进引入、验证收益"。

模式引入应"滞后于复杂度"而非"超前于需求"。渐进重构、验证收益是避免过度设计的关键。

#
★★

49. 将多个行为型模式叠加使用时,哪些组合易引发循环依赖与状态不一致,应如何通过分层治理

将多个行为型模式叠加使用时,哪些组合易引发循环依赖与状态不一致,应如何通过分层治理?

  • 模式叠加的循环依赖
  • 状态不一致
  • 分层治理

多个行为型模式叠加时,易引发循环依赖的组合:观察者 + 中介者(中介者通知观察者,观察者又触发中介者)、状态机 + 命令(命令回调中改状态导致重入)、责任链 + 策略(链节点互相引用策略造成耦)。状态不一致源于"回调重入"与"共享状态多处修改"。治理方法:分层——把模式按职责分层(如外层策略、内层状态机),让依赖单向;用事件/消息解耦避免循环引用;状态变更集中管理(单一状态源),避免多模式竞争修改。

模式叠加的循环依赖与状态不一致,靠"单向依赖分层 + 集中状态管理"治理。

#
★★

50. 应如何在不破坏开闭原则的前提下权衡抽象层级

应如何在不破坏开闭原则(OCP)的前提下权衡抽象层级?

  • OCP 与抽象
  • 抽象层级权衡
  • 过度抽象

在不破坏 OCP 前提下权衡抽象层级:抽象应覆盖"变化的部分",让系统对扩展开放;但抽象层级过高会引入抽象成本与复杂性。权衡:只在真实变化点抽象(接口),稳定部分不抽象;抽象层级与应用复杂度匹配——简单系统用简单抽象,复杂系统用分层抽象。避免"为抽象而抽象"(过度层级)与"无抽象"(变化点难扩展)。OCP 靠"变化点抽象"实现,抽象层级以"变化频率"为衡量。

抽象层级以"变化点"为衡量,变化才抽象,稳定不抽象。避免过度层级破坏简洁性。

#
★★

51. 模板方法模式(Template Method)在 Spring JdbcTemplate/RestTemplate 的钩子方法设计

请说明模板方法模式(Template Method)在 Spring JdbcTemplate/RestTemplate 的钩子方法设计?

  • 模板方法的骨架
  • 钩子/回调
  • JdbcTemplate/RestTemplate

JdbcTemplate 和 RestTemplate 是模板方法模式的体现:它们定义"执行流程"的骨架(JdbcTemplate:获取连接 → 执行 SQL → 处理结果 → 释放资源;RestTemplate:构建请求 → 执行 → 解析响应),把"变化的部分"(SQL 语句、结果映射、请求参数)作为回调/钩子(如 RowMapperResponseExtractor)交给调用方实现。这样流程固定,变化点由回调注入,是"模板方法 + 回调"的结合。

模板方法固定流程,回调注入变化点。JdbcTemplate 的 RowMapper、RestTemplate 的 ResponseExtractor 是钩子。

#
★★

52. 模板方法的钩子若能改变核心不变量,为什么说明抽象边界设计有问题,如何重构为组合或策略

模板方法的钩子若能改变核心不变量,为什么说明抽象边界设计有问题,如何重构为组合或策略?

  • 钩子改变核心不变量
  • 抽象边界问题
  • 重构为组合/策略

模板方法的钩子若能改变核心不变量(如算法步骤的关键约束、结果的一致性),说明抽象边界设计有问题:本应固定的"骨架不变量"被暴露给子类改变,破坏了模板方法"固定骨架"的初衷。重构:把可能变化的"核心逻辑"从模板中抽离,改为组合(委托给策略/函数)而非继承钩子;或把变化的部分用策略模式注入,让不变量留在模板、易变逻辑交给策略。这样不变量受控,变化被隔离到策略。

钩子改变不变量说明"变化点放错了位置"。用组合/策略把易变逻辑外移,保留不变量。

#
★★

53. 状态模式在领域驱动设计中与显式状态机(Spring Statemachine)的协作

请说明状态模式在领域驱动设计(DDD)中与显式状态机(Spring Statemachine)的协作?

  • 状态模式
  • Spring Statemachine
  • DDD 协作

状态模式把状态相关的行为封装到状态对象中,状态转移时行为变化;Spring Statemachine 提供显式的状态机(状态、事件、迁移、动作),支持状态机的显式建模。在 DDD 中,二者协作:领域实体用状态机建模状态转移(把实体状态机化),保证非法迁移被阻止;状态机事件触发领域动作(用户动作、领域事件)。Spring Statemachine 提供状态转移的显式控制与持久化,状态模式提供状态行为的封装,共同支撑领域状态的一致演进。

状态模式封装"状态行为",Spring Statemachine 提供"显式迁移"。DDD 中结合实现状态一致。

#
★★

54. 用责任链实现审批流时,规则热更新期间如何保证一个实例始终使用同一版本的链

用责任链实现审批流时,规则热更新期间如何保证一个实例始终使用同一版本的链?

  • 规则热更新
  • 同一版本链
  • 一致性

责任链实现审批流时,规则可能热更新,若更新期间链被重新加载,同一审批实例可能使用不同版本的链,导致行为不一致。保证同一实例使用同一版本链:链版本化(规则带版本号,加载时绑定版本);实例启动时固定链版本(捕获链版本快照,不经更新);更新时对新实例用新链、旧实例继续用旧链(版本隔离)。用"版本 + 快照 + 灰度"保证实例一致性。

热更新一致性靠"版本绑定"。实例在创建时固定链版本,更新只影响新实例。

#
★★

55. 策略模式、状态模式和模板方法都能封装行为时,如何依据变化轴、状态归属和扩展边界做出选择

策略模式、状态模式和模板方法都能封装行为时,如何依据变化轴、状态归属和扩展边界做出选择?

  • 变化轴
  • 状态归属
  • 扩展边界

三者都能封装行为,选择依据:策略模式适合"算法可替换、无状态"(变化轴是算法族,按策略选择);状态模式适合"行为随状态变化"(状态归属明确,状态决定行为);模板方法适合"算法骨架固定、部分步骤变化"(变化轴是步骤,扩展边界是钩子)。判断:如果行为随状态变化选状态模式;如果只是算法替换选策略;如果流程固定只变步骤选模板方法。按"变化轴"与"状态归属"定位。

变化轴决定选择:策略管"算法替换"、状态管"状态驱动行为"、模板管"步骤变化"。状态归属是关键区分。

#
★★

56. 策略模式与函数式接口 Function<T,R> 在 JDK 8+ 下如何评估抽象成本与可测性的真实差异

策略模式与函数式接口 Function<T,R> 在 JDK 8+ 下如何评估抽象成本与可测性的真实差异?

  • 策略模式的抽象成本
  • 函数式接口
  • 可测性

策略模式需要定义策略接口、多个实现类,抽象成本高(类多、样板多);函数式接口(Function/Predicate)用 Lambda 直接表达策略,抽象成本低、简洁。可测性:策略模式各策略是独立类,可单独单元测试;函数式接口的 Lambda 逻辑内联,测试需提取或依赖语言特性。真实差异:简单策略用函数式接口成本低、可测性相似(Lambda 可提取为方法测试);复杂策略(含状态、多行为)用策略类更清晰。权衡"成本"与"复杂度"。

函数式接口降低抽象成本,策略类适合复杂策略。可测性差异通过提取方法可弥补。

#
★★

57. 策略模式在 Spring @Profile 的应用

请说明策略模式在 Spring @Profile 中的应用?

  • @Profile 的条件激活
  • 策略选择
  • 多环境

Spring 的 @Profile 按环境条件(dev/prod/test)激活/停用 Bean,可用于策略选择:不同环境注册不同的策略实现 Bean,激活的 profile 决定注入哪个策略。例如 dev 用内存实现、prod 用生产实现,通过 profile 区分。@Profile 是"条件驱动的策略装配",让策略 Bean 按环境选择,是策略模式与 Spring 条件装配的结合。工程上用于多环境差异化配置与实现。

@Profile 按环境激活策略 Bean,实现"按环境选择策略",是策略模式的条件化。

#
★★

58. 策略模式(Strategy)与 Comparator 的应用

请说明策略模式(Strategy)与 Comparator 的应用?

  • Comparator 作为策略
  • 排序策略
  • 可替换

Comparator 是策略模式的典型应用:排序算法接受一个 Comparator 作为"排序策略",调用方传入不同的比较策略(按姓名、按年龄、按分数),决定排序行为。Comparator 是策略接口,各种比较实现是策略,Collections.sort/Arrays.sort 是策略的使用者。这体现了"算法骨架固定(排序)、策略可替换(比较方式)"。Comparator 是 Java 标准库中策略模式的最佳范例。

Comparator 是策略接口,排序通过传入不同比较策略改变行为,是策略模式的经典应用。

#
★★

59. 行为型模式如何与可配置化策略中心协作以减少硬编码分支

行为型模式如何与可配置化策略中心协作以减少硬编码分支?

  • 可配置化策略中心
  • 减少硬编码分支
  • 协作

可配置化策略中心把策略的注册/选择集中到配置(如配置项、数据库、规则引擎),行为型模式(策略、责任链)提供"策略/链"的结构,二者协作:策略中心根据配置决定用哪个策略、链如何组装,行为型模式提供执行逻辑,减少 if-else 硬编码。例如策略注册表按配置选择策略,规则引擎按规则组装责任链。这样新增策略/规则只需改配置,无需改代码分支,符合 OCP。

策略中心管"选择与注册",行为型模式管"执行结构",配置驱动消除硬编码分支。

#
★★

60. 行为型模式如何被 Event Sourced Aggregate 重新表达

行为型模式如何被 Event Sourced Aggregate 重新表达?

  • Event Sourcing
  • Aggregate
  • 行为表达

在 Event Sourced Aggregate 中,行为型模式被重新表达:命令模式通过"命令 → 领域事件"表达(命令执行产生事件而非直接改状态);状态机通过"事件序列驱动状态"表达(状态由事件推断);观察者通过"事件发布/订阅"表达。聚合根接收命令、验证、发布事件并持久化,行为通过事件流表达,而非内部状态机。这样行为型模式在事件溯源中转化为"事件驱动"的表达,可重放、可审计。

事件溯源把行为表达为"事件",命令产生事件、状态由事件推导,行为型模式被事件化。

#
★★

61. 观察者模式在 Spring Modulith 模块事件(@ApplicationEventListener)

请说明观察者模式在 Spring Modulith 模块事件(@ApplicationEventListener)中的应用?

  • Spring Modulith 模块事件
  • @ApplicationModuleListener
  • 模块解耦

Spring Modulith 用模块事件实现模块间解耦,观察者模式是其基础:模块发布领域事件,其他模块通过 @ApplicationModuleListener 监听,Spring 把事件路由到监听器。Modulith 的事件是"模块间通信"的通道,让模块无需直接依赖即可响应彼此事件,符合观察者模式"发布-订阅"思想。Modulith 还提供事件发布失败的记录与回放,保证模块事件的可靠性。

Modulith 模块事件是观察者模式的模块化应用,模块间通过事件解耦,事件可靠投递。

#
★★

62. 观察者模式与发布-订阅(消息总线)在耦合度与异步性上的差异

请说明观察者模式与发布-订阅(消息总线)在耦合度与异步性上的差异?

  • 耦合度
  • 异步性
  • 差异

观察者模式(Observer)中,被观察者直接持有观察者引用,耦合度高(被观察者知道观察者),通常是同步调用;发布-订阅(消息总线/中间件)中,发布者通过总线/主题发布,不持有订阅者引用,耦合度低(发布者与订阅者完全解耦),通常是异步。差异:Observer 同步、进程内、耦合较高;发布-订阅异步、可跨进程、松耦合。二者思想相关但耦合度与异步性不同,按解耦需求选择。

观察者直接引用观察者(高耦合、同步),发布订阅经总线解耦(低耦合、异步)。

#
★★

63. 命令模式与消息队列的相似性,将请求封装为可排队、可重放的对象

请说明命令模式与消息队列的相似性:将请求封装为可排队、可重放的对象?

  • 命令封装请求
  • 可排队、可重放
  • 与消息队列

命令模式把请求封装为对象,与消息队列把消息封装为可排队对象非常相似:命令可排队(放入队列/线程池)、可重放(重新执行);消息也可排队、可重放(消费者处理失败重投)。命令模式在进程内提供"可排队、可重放、可审计"的语义,消息队列在分布式提供同样的能力。二者思想一致——把"请求"对象化以便排队、重放、解耦,命令模式是进程内版本,消息队列是分布式版本。

命令与消息都"把请求对象化",支持排队重放。命令是进程内,消息是分布式。

#

64. 观察者模式(Observer)与 Spring 事件

请说明观察者模式(Observer)与 Spring 事件的关系?

  • 观察者模式
  • Spring 事件
  • 实现

Spring 事件机制(ApplicationEventPublisher + ApplicationListener)是观察者模式的实现:发布者发布事件,监听器监听,通过 ApplicationEventMulticaster 分发。观察者模式是概念原型,Spring 事件是其工程化(支持异步、事务、泛型事件)。Spring 事件让业务组件解耦,发布者无需知道监听者,是观察者模式在 Spring 生态的落地。

Spring 事件是观察者模式的工程实现,增强异步、事务、泛型等能力。

#

65. 解释器模式执行用户提供的 SpEL 或规则表达式时,如何限制语法、函数、反射能力和资源消耗以防注入

解释器模式执行用户提供的 SpEL 或规则表达式时,如何限制语法、函数、反射能力和资源消耗以防注入?

  • SpEL 解释
  • 限制语法与函数
  • 防止注入与资源消耗

解释用户提供的 SpEL/规则表达式时,需限制:语法(只允许受限的表达式语法,禁止危险操作)、函数(只暴露白名单函数,禁止任意方法调用)、反射能力(禁用反射类访问,如 T() 类引用、new 构造)、资源消耗(限制表达式长度、执行次数/超时,防止拒绝服务)。用 SpEL 的 EvaluationContext 定制 PropertyAccessorMethodResolver 限制可访问的类与方法,并设置表达式长度与执行超时。这样防止注入与资源耗尽。

解释器执行外部表达式需"白名单 + 限制反射 + 限资源",防注入与 DoS。

#

66. 解释器模式(Interpreter)与表达式求值

请说明解释器模式(Interpreter)与表达式求值?

  • Interpreter 的语法树
  • 表达式求值
  • 适用场景

解释器模式为特定语言定义语法结构,用表达式类组合构成语法树,解释器遍历语法树求值。表达式求值(算术、规则、SQL 过滤)是解释器的典型应用:表达式被解析为语法树,节点表示操作数/运算符,解释器递归求值。适用场景:语法简单、频繁变化、需要解释的表达式。但解释器实现复杂,现代常用现成表达式引擎(SpEL、EL、规则引擎)替代手写解释器。

解释器把表达式解析为语法树并求值,适合简单语法。复杂表达式用现成引擎更合适。

#

67. 访问者模式便于新增操作却增加新增节点成本,结合 Java 25 密封类型和模式匹配 switch 时如何权衡

访问者模式便于新增操作却增加新增节点成本,结合 Java 25 密封类型和模式匹配 switch 时如何权衡?

  • 访问者的操作/节点成本
  • 密封类型
  • 模式匹配 switch

访问者模式便利"新增操作"(新增 Visitor 即可)但增加"新增节点"成本(需改所有 Visitor);Java 25 的密封类型(sealed)限制类型集合,模式匹配 switch 用 case 分支遍历类型。权衡:用密封类型 + 模式匹配 switch 替代访问者,新增操作只需新增 switch case(无需改节点类型),编译期可检查是否穷尽(sealed 保证)。但 switch 集中的操作不如访问者分散。若操作频繁变化、类型固定,用模式匹配 switch 更简洁;若需操作分散、类型确定性,访问者仍可用。密封类型让 switch 穷尽性可编译期验证。

密封类型 + 模式匹配 switch 提供"新增操作只需加 case"的替代,且穷尽性编译期可查。按操作变化频率权衡。

#

68. 访问者模式(Visitor)与 AST 遍历

请说明访问者模式(Visitor)与 AST 遍历的关系?

  • Visitor 遍历
  • AST 结构
  • 分离操作

访问者模式常用于 AST(抽象语法树)遍历:AST 由不同类型的节点组成,Visitor 定义访问各节点的方法,遍历时对每个节点调用对应的 visit 方法,实现"遍历 + 操作分离"。编译器、解释器、静态分析用 Visitor 遍历 AST,执行代码生成、类型检查、语义分析等操作,无需修改 AST 节点类。Visitor 让"数据结构稳定(AST 节点类型固定)、操作多变"的场景可扩展。

AST 节点类型固定、操作多变,正是访问者的适用场景。遍历与操作分离,新增操作易扩展。

#

69. 访问者遍历可变 AST 时,边遍历边改写会破坏哪些假设,如何采用不可变重写结果

访问者遍历可变 AST 时,边遍历边改写会破坏哪些假设,如何采用不可变重写结果?

  • 可变 AST 边遍历边改写
  • 破坏的假设
  • 不可变重写

访问者遍历可变 AST 时边遍历边改写会破坏假设:遍历顺序依赖(改写影响后续遍历)、父子一致性(改写后节点引用失效)、不变式(AST 结构约束被破坏)。破坏可能导致遍历结果不确定或状态不一致。采用不可变重写:遍历时返回新节点(不可变重写),而非就地修改原 AST,生成新的不可变 AST 作为结果,原 AST 保持不变。这样遍历安全、可重试、并发安全,避免边遍历边改写的副作用。

边遍历边改写破坏遍历与结构一致性,用不可变重写(返回新树)保证安全与可重放。

#

70. 请用具体业务场景说明,命令模式与事件驱动在审计追溯、重放与可观测性上如何互补

请用具体业务场景说明,命令模式与事件驱动在审计追溯、重放与可观测性上如何互补?

  • 命令模式的审计
  • 事件驱动的重放
  • 可观测性互补

以"订单处理"为例:命令模式把"下单、支付、退款"封装为命令对象,记录命令执行(谁、何时、什么操作),提供审计追溯;事件驱动记录"订单已创建、已支付"等领域事件,事件可重放还原状态。二者互补:命令提供"操作意图"的审计(做了什么),事件提供"状态变化"的可重放(状态如何演变),结合可观测性(trace、日志把命令与事件串联),既能审计操作、又能重放状态、还能观测链路。命令与事件互补实现完整可观测。

命令管"操作意图",事件管"状态变化",trace 串联,三者互补实现审计、重放与可观测。

#

71. 责任链在 Resilience4j 的应用

请说明责任链在 Resilience4j 中的应用?

  • Resilience4j 的容错
  • 责任链
  • 组合

Resilience4j 用装饰器(Decorator)组合容错能力,可理解为责任链的变体:CircuitBreakerRateLimiterRetryBulkheadTimeLimiter 等容错组件可组合成链,请求依次经过各组件,每个组件处理自己的容错逻辑(熔断、限流、重试、隔离、超时),可短路(熔断直接拒绝)。组合顺序决定容错行为。Resilience4j 的装饰器链让容错能力按序叠加,是责任链思想的工程应用。

Resilience4j 的装饰器链组合熔断/限流/重试等,请求按序经过各容错组件,是责任链的容错化。

#

72. 责任链模式在 Spring WebFlux WebFilter 与 Servlet Filter 链的应用

请说明责任链模式在 Spring WebFlux WebFilter 与 Servlet Filter 链的应用?

  • WebFilter 链
  • 响应式链
  • 责任链

Servlet Filter 与 WebFlux WebFilter 都是责任链模式:请求经过过滤器链,每个过滤器处理或放行。Servlet Filter 基于阻塞线程,链通过 filterChain.doFilter() 传递;WebFlux WebFilter 基于响应式,链通过 WebFilterChain.filter(exchange) 返回 Mono,异步传递。二者都是责任链,但 WebFlux 是响应式非阻塞的链,支持异步传递。责任链让横切关注点(鉴权、日志、CORS)按序处理。

两种 Filter 链都是责任链,Servlet 同步、WebFlux 响应式异步。横切关注点用链组织。

#

73. 迭代器包装数据库游标时,短路遍历、异常和 close 应如何协作,才能避免连接泄漏和重复关闭

迭代器包装数据库游标时,短路遍历、异常和 close 应如何协作,才能避免连接泄漏和重复关闭?

  • 游标迭代器
  • 短路与异常
  • close 协作

迭代器包装数据库游标时,需保证资源释放:遍历到尽头(短路)时 close;迭代中途异常时 close;调用方主动停止(提前退出)时 close。用一个"统一关闭"机制(如 finally 或包装在 try-with-resources)保证无论短路、异常还是提前退出都关闭游标,且 close 需幂等(重复关闭不报错/不重复释放)。迭代器应把 close 委托给持有者,避免每处都手动关闭导致泄漏或重复关闭。

保证"任何退出路径都 close + close 幂等"是游标迭代器的关键,避免连接泄漏与重复关闭。

#

74. 迭代器模式(Iterator)与 Iterator/Spliterator

请说明迭代器模式(Iterator)与 Iterator/Spliterator 的关系?

  • Iterator 模式
  • Spliterator
  • 并行

迭代器模式(Iterator)提供"统一方式遍历集合",Iterator 接口是它的实现(hasNext/next)。Spliterator 是迭代器的增强,支持:tryAdvance 按需推进、trySplit 分割(支持并行遍历)、特性(有序、大小、非空)。Spliterator 在 Iterator 基础上加入"分割与并行"能力,使 Stream 并行流能分段遍历。迭代器模式提供"遍历抽象",Spliterator 扩展为"可分割可并行的遍历"。

Iterator 是"顺序遍历",Spliterator 是"可分割可并行遍历"。二者是迭代器模式的演进。

#

75. 选择行为型模式时,如何用变化轴、调用方向和生命周期判断,而不是按类图机械套用

选择行为型模式时,如何用变化轴、调用方向和生命周期判断,而不是按类图机械套用?

  • 变化轴
  • 调用方向
  • 生命周期

选择行为型模式,应基于"变化轴、调用方向、生命周期"判断而非类图:变化轴(什么会变——算法、状态、行为、步骤),决定用策略/状态/模板;调用方向(谁调用谁——控制反转、回调),决定用观察者/命令/中介者;生命周期(对象的创建、共享、销毁),决定单例/原型/池。按这三个维度分析"系统哪里会变、调用关系如何、对象生命周期",再选匹配的模式,避免机械套用类图导致模式与问题不匹配。

变化轴定"策略/状态/模板",调用方向定"观察者/命令/中介",生命周期定"创建/共享"。先分析问题再选模式。

#

76. JEP 485 Stream Gatherers 与传统 Iterator/Spliterator 在中间状态、窗口聚合和并行处理方面有何差异

请说明 JEP 485 Stream Gatherers 与传统 Iterator/Spliterator 在中间状态、窗口聚合和并行处理方面的差异?

  • Stream Gatherers
  • 中间状态
  • 窗口聚合与并行

JEP 485 Stream Gatherers 引入 Gatherer 接口,允许自定义流中间操作(如 windowfoldgroupBy),比传统 Iterator/Spliterator 更灵活地表达中间状态与窗口聚合。差异:Gatherers 支持有状态的中间操作(维护中间状态、窗口/滑动聚合),且通过 Stream 的并行框架自动支持并行;传统 Iterator/Spliterator 是底层遍历,需手动处理状态与并行。Gatherers 把"中间状态、窗口聚合"提升为声明式流操作,并集成并行。

Gatherers 提供声明式有状态中间操作(窗口聚合),并行由 Stream 框架支持,优于手动遍历。

#

77. 观察者在 java.util.Observable(已弃用)与 PropertyChangeListener

请说明观察者在 java.util.Observable(已弃用)与 PropertyChangeListener 中的实现?

  • Observable 已弃用
  • PropertyChangeListener
  • 替代

java.util.Observable/Observer 是 Java 内置的观察者实现,但已弃用,因为其设计不佳(可继承性限制、事件无类型、性能)。PropertyChangeListener/PropertyChangeSupport(JavaBeans)是更受推荐的替代:提供属性变化监听,支持绑定/属性变更通知,类型更明确。工程上更常用 Spring 事件、自定义事件监听器或反应式(如 RxJava)替代 Observable。PropertyChangeListener 用于属性级观察,比 Observable 更精细。

Observable 已弃用,PropertyChangeListener 提供属性级监听,现代更常用 Spring 事件等。

#

78. 访问者模式(Visitor)在 JDK 25 模式匹配与 Java AST 处理(如 JavaParser)

请说明访问者模式(Visitor)在 JDK 25 模式匹配与 Java AST 处理(如 JavaParser)中的应用?

  • Visitor 遍历 AST
  • JavaParser
  • 模式匹配

JavaParser 等库用 Visitor 模式遍历 Java AST:VoidVisitor 提供 visit(MethodDeclaration)visit(ClassOrInterfaceDeclaration) 等方法,工具代码(静态分析、代码生成)通过 Visitor 访问 AST 节点,无需修改 AST 类。JDK 25 的模式匹配(pattern matching)为访问者提供了补充:用 switch 模式匹配类型分支,可替代部分 Visitor 的 visit 方法。但 JavaParser 的 AST 节点类型固定、操作多变,仍适合 Visitor;模式匹配适合需要穷尽类型检查的场景。二者可结合。

JavaParser 用 Visitor 遍历 AST,模式匹配 switch 提供类型分支的替代,按操作需求选择。

#

79. 责任链在日志框架(Logger 层级与 Appender 链)中的应用

请说明责任链在日志框架(Logger 层级与 Appender 链)中的应用?

  • Logger 层级
  • Appender 链
  • 责任链

日志框架(Log4j2、Logback)体现责任链:Logger 按层级继承(root → 子 logger),日志事件沿层级向上传播(若未拦截),各层 Logger 可处理(如设置级别);Appender 可组成链,日志事件依次经过 Appender(输出到控制台、文件、远程)。责任链让日志处理分级、可配置、可组合。Logger 层级传播与 Appender 链是责任链思想在日志框架的应用。

Logger 层级传播(additivity)与 Appender 链是责任链,日志事件按层级/链处理。