字数:约4200字 | 阅读时间:12分钟“设计模式不是银弹,但理解模式演进的逻辑,能让你在正确的场景做出正确的选择。”
引言 十年前,我们学习设计模式时,总会先从”四人帮”的23种模式入手。那时候,设计模式更多是一种通用的编程语言底层知识,适用于C++、Java、C#等面向对象语言。
但2026年的今天,云原生、响应式编程、函数式接口、依赖注入容器已成主流。Spring Boot 3.4.x、Java 21的sealed class和record,已经从语法层面原生支持了许多曾经需要”设计模式”才能实现的功能。
设计模式没有消失——它在演进 。
本文以策略模式为叙事主线,串联装饰器模式、责任链模式、工厂模式,展示设计模式在现代Java生态中的形态变化,以及何时该用、何时该”绕路”。
一、设计模式的本质:封装变化 所有设计模式的核心逻辑只有一条:把变化的部分封装起来,让不变的部分稳定复用 。
这条原则最早由Gamma等人总结为”对变化的封装”(Encapsulate Varies),后来GoF的《设计模式》将其拆解为23种具体形态。策略模式、装饰器模式、责任链模式、工厂方法——它们解决的都是同一类问题的不同切面。
什么是”变化” 在业务代码中,”变化”通常表现为以下几种形态:
变化类型
表现
典型模式
算法/策略变化
不同算法可替换执行
策略模式
功能叠加变化
运行时动态添加行为
装饰器模式
处理链变化
多个处理器顺序执行
责任链模式
对象创建变化
创建逻辑需要灵活替换
工厂模式
理解”变化”的本质,决定了你是否真的需要设计模式——而不是用模式来”装饰”一段本就简单的代码。
二、策略模式的演进:策略注射(Strategy Injection) 2.1 传统策略模式的问题 传统策略模式的结构很清晰:
1 2 3 4 Context → 持有 → Strategy接口 ↑ ConcreteStrategyA ConcreteStrategyB
一个典型的实现:
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 public interface PayStrategy { void pay (double amount) ; } public class AlipayStrategy implements PayStrategy { public void pay (double amount) { System.out.println("支付宝支付:" + amount); } } public class WechatPayStrategy implements PayStrategy { public void pay (double amount) { System.out.println("微信支付:" + amount); } } public class PaymentContext { private PayStrategy strategy; public void setStrategy (PayStrategy strategy) { this .strategy = strategy; } public void execute (double amount) { strategy.pay(amount); } }
这段代码有几个问题:
策略的创建与使用耦合 :PaymentContext本身不负责创建策略,但在实际业务中,策略往往需要依据业务参数(用户等级、渠道标识、金额区间)来选择
策略选择逻辑分散 :选择哪个策略的if-else或switch散落在代码各处
策略对象生命周期管理混乱 :策略是每次新建还是复用?没有统一约定
2.2 策略注射:从外部注入到自动装配 Java 21配合Spring Boot 3.4.x,给我们提供了更好的解法——策略注射(Strategy Injection) ,也叫”策略寻址”(Strategy Resolution)。
核心思路:不再让调用方自己创建策略,而是通过依赖注入框架,根据运行时上下文自动注入合适的策略实现 。
Spring的Map注入提供了这个能力:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Service public class PaymentService { private final Map<String, PayStrategy> strategyMap; public PaymentService (Map<String, PayStrategy> strategyMap) { this .strategyMap = strategyMap; } public void pay (String channel, double amount) { PayStrategy strategy = strategyMap.get(channel); if (strategy == null ) { throw new IllegalArgumentException ("未知支付渠道:" + channel); } strategy.pay(amount); } }
配置层:
1 2 3 4 5 6 7 8 9 10 11 12 @Configuration public class PayConfig { @Bean public PayStrategy alipay () { return new AlipayStrategy (); } @Bean public PayStrategy wechat () { return new WechatPayStrategy (); } }
调用方只需传入"alipay"或"wechat"字符串,Spring自动完成策略寻址。这比传统的if-else清晰得多——选择逻辑和执行逻辑完全分离。
2.3 更进一步:策略的选择由框架完成 更进一步,我们可以让Spring根据条件自动选择策略,而不需要调用方传入任何标识。
利用@ConditionalOnProperty:
1 2 3 4 5 @Component @ConditionalOnProperty(name = "payment.default-channel", havingValue = "alipay") public class DefaultAlipayStrategy implements PayStrategy { }
或者更优雅地使用Spring的ApplicationContext进行动态策略查找:
1 2 3 4 5 6 7 8 @Service public class DynamicStrategyResolver { private final ApplicationContext ctx; public PayStrategy resolve (Class<? extends PayStrategy> type) { return ctx.getBean(type); } }
2.4 Java 21的加持:sealed class让策略选择更安全 Java 21引入的sealed class和pattern matching,让策略模式的类型安全提升了一个台阶:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public sealed interface PayStrategy permits AlipayStrategy, WechatPayStrategy, CreditCardStrategy { double calculateFee (double amount) ; } public record AlipayStrategy (double discountRate) implements PayStrategy { public double calculateFee (double amount) { return amount * (1 - discountRate); } } PayStrategy s = getStrategy();switch (s) { case AlipayStrategy a -> System.out.println("支付宝费:" + a.calculateFee(amount)); case WechatPayStrategy w -> System.out.println("微信费率:" + w.calculateFee(amount)); case CreditCardStrategy c -> System.out.println("信用卡费:" + c.calculateFee(amount)); }
sealed接口确保了策略类型的有限性,而pattern matching保证了编译器在编译期就能发现未处理的分支——这是Java 21带给设计模式的语言级进化。
三、装饰器模式在Spring Boot拦截器链的应用 装饰器模式的核心是在运行时动态包装对象,添加额外行为,且不改变原接口 。
在Spring Boot中,拦截器链(InterceptorChain)就是装饰器模式的现代变体。
3.1 传统装饰器 传统装饰器长这样:
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 public interface DataService { String getData () ; } public class BasicDataService implements DataService { public String getData () { return "原始数据" ; } } public class LoggingDecorator implements DataService { private final DataService delegate; public LoggingDecorator (DataService delegate) { this .delegate = delegate; } public String getData () { System.out.println("调用前记录日志" ); String result = delegate.getData(); System.out.println("调用后记录日志" ); return result; } } public class CacheDecorator implements DataService { private final DataService delegate; private final Map<String, String> cache = new HashMap <>(); public CacheDecorator (DataService delegate) { this .delegate = delegate; } public String getData () { return cache.computeIfAbsent("key" , k -> delegate.getData()); } }
层层嵌套:new CacheDecorator(new LoggingDecorator(new BasicDataService()))——这就是经典的装饰器链。
3.2 Spring Boot的拦截器链:自动代理的装饰器 Spring Boot 3.4.x中的拦截器链,是装饰器模式在框架层的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Component public class TimingInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest req, HttpServletResponse res, Object handler) { req.setAttribute("startTime" , System.nanoTime()); return true ; } @Override public void afterCompletion (HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) { long elapsed = System.nanoTime() - (long ) req.getAttribute("startTime" ); System.out.println("请求耗时:" + elapsed + "ns" ); } }
注册到Spring MVC:
1 2 3 4 5 6 7 8 9 10 11 12 @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired private TimingInterceptor timingInterceptor; @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor(timingInterceptor) .addPathPatterns("/api/**" ) .excludePathPatterns("/health" ); } }
Spring的HandlerInterceptor本质上就是一个装饰器——它在不改变Controller行为的前提下,为请求处理添加了计时、日志、鉴权等横切关注点。InterceptorChain负责按顺序调用这些拦截器,并在适当位置让原始处理逻辑继续执行。
3.3 拦截器 vs 装饰器:选择哪个
场景
推荐
HTTP请求级别的横切逻辑(日志、计时、鉴权)
Spring拦截器
业务方法级别的行为增强(缓存、重试、熔断)
装饰器模式
需要在编译时确定类型关系
装饰器(显式类型)
需要在运行时灵活增删行为
拦截器(通过配置)
四、责任链模式在中间件框架的实践 责任链模式(Chain of Responsibility)将多个处理器串联起来,每个处理器决定处理请求或传递给下一个处理器。
在中间件框架中,这个模式无处不在——Netty的Pipeline、Servlet Filter、以及Spring Security的过滤器链,都是责任链的变体。
4.1 从Servlet Filter到Spring Security过滤器链 最朴素的Servlet Filter实现:
1 2 3 4 5 6 7 8 9 10 11 12 public class AuthFilter implements Filter { @Override public void doFilter (ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpReq = (HttpServletRequest) req; if (httpReq.getHeader("Authorization" ) == null ) { ((HttpServletResponse) res).sendError(401 , "Unauthorized" ); return ; } chain.doFilter(req, res); } }
Spring Boot 3.4.x中的Spring Security过滤器链,提供了更强大的责任链抽象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain (HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(auth -> auth .requestMatchers("/health" ).permitAll() .requestMatchers("/api/**" ).authenticated() ) .addFilterBefore(new ApiKeyAuthFilter (), UsernamePasswordAuthenticationFilter.class) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); return http.build(); } }
Spring Security的过滤器链由多个Filter组成,每个Filter负责自己的处理范围——认证、授权、会话管理、CORS——通过责任链串联,最终到达目标Controller。
4.2 自定义责任链:Spring的@Order和@Primary 在自定义业务场景中,Spring的@Order注解和List注入让我们可以轻松构建可插拔的责任链:
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 public interface OrderHandler { boolean canHandle (Order order) ; void handle (Order order) ; int getOrder () ; } @Service public class OrderProcessChain { private final List<OrderHandler> handlers; public OrderProcessChain (List<OrderHandler> handlers) { this .handlers = handlers.stream() .sorted(Comparator.comparingInt(OrderHandler::getOrder)) .toList(); } public void process (Order order) { for (OrderHandler handler : handlers) { if (handler.canHandle(order)) { handler.handle(order); } } } }
这样添加新的处理器只需要实现OrderHandler接口并加上@Order——无需修改调用方代码。责任链的扩展性在Spring的帮助下变得极其轻量。
五、工厂模式与依赖注入的对比 工厂模式和依赖注入解决的问题本质上相同:对象创建与对象使用解耦 。
5.1 工厂模式的现代价值 在依赖注入容器出现之前,工厂模式是管理对象创建的主要手段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public interface PaymentFactory { PayStrategy create (String channel) ; } @Component public class PaymentFactoryImpl implements PaymentFactory { @Override public PayStrategy create (String channel) { return switch (channel) { case "alipay" -> new AlipayStrategy (); case "wechat" -> new WechatPayStrategy (); default -> throw new IllegalArgumentException (channel); }; } }
工厂模式的价值在于创建逻辑的集中管理 ——当对象创建涉及复杂条件判断、资源加载或参数验证时,工厂仍然不可替代。
5.2 依赖注入的替代 但Spring的依赖注入已经替代了大部分工厂场景:
1 2 3 4 5 6 7 @Autowired private AlipayStrategy alipayStrategy;@Autowired private Map<String, PayStrategy> strategies;
什么时候仍然需要工厂模式?
创建逻辑涉及条件判断、参数验证、资源分配
需要延迟创建 (Lazy Initialization)而非启动时注入
创建逻辑涉及第三方库 ,无法为其添加@Component注解
Spring的ObjectProvider和FactoryBean接口也为此提供了补充能力。
5.3 对比总结
维度
工厂模式
依赖注入
创建控制权
调用方控制
容器控制
适用场景
复杂创建逻辑、延迟创建
标准单例或原型作用域
灵活性
高(可自定义逻辑)
中(受容器约束)
可测试性
好(可注入Mock)
好
Spring集成
可作为@Bean注册
直接@Autowired
在Spring Boot 3.4.x生态下,依赖注入是默认选择,工厂模式作为补充——当注入解决不了问题时,工厂才是答案。
六、如何判断过度设计 设计模式是工具,不是目标。以下信号说明你可能正在过度设计:
信号一:模式嵌套超过三层 如果你的代码是 new A(new B(new C(new D()))),先问自己——真的需要四个装饰器吗?
信号二:用策略模式替代简单的switch 策略模式的优势在于运行时可替换 和上下文无关 。如果你只有两个固定的分支,且不会在运行时切换,switch语句比接口+多实现的策略模式简洁得多。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public double calculateTax (Income income) { Map<String, TaxStrategy> strategies = ctx.getBeansOfType(TaxStrategy.class); return strategies.get(income.getCountry()).calculate(income); } public double calculateTax (Income income) { return switch (income.getCountry()) { case "CN" -> income.getAmount() * 0.13 ; case "US" -> income.getAmount() * 0.10 ; default -> 0 ; }; }
信号三:接口只有一个实现 如果PayStrategy只有一个实现类AlipayStrategy,那接口本身就是多余。直接用AlipayStrategy,除非你确定未来会有其他实现。
信号四:为了使用模式而引入模式 “这个问题能不能用责任链?”——如果第一反应是模式名称而不是问题本身,说明你正在被模式反向驱动。
信号五:测试覆盖率依赖模式而非业务 模式应该让代码更容易测试。如果你的测试需要理解一堆设计模式才能写,那模式本身可能成了障碍。
判断标准:简单可读原则(YAGNI + KISS) Kent Beck的YAGNI(You Aren’t Gonna Need It)和KISS(Keep It Simple, Stupid)原则在这里依然适用:先用最简单的方式实现,等变化的本质暴露了,再用模式重构 。
设计模式是需求变化的响应,而不是预防性措施。把模式当作”债务”——必要时刻的杠杆,用完记得偿还(重构简化)。
结语 设计模式在2026年的Java生态中,角色没有改变——仍然是封装变化的工具。但实现方式已经大幅进化:
策略模式 → 从手动注入到Spring Map注入,再到sealed class的编译期穷举校验
装饰器模式 → 从手动包装到Spring拦截器链的自动代理
责任链模式 → 从回调嵌套到Spring FilterChain的可配置插拔
工厂模式 → 从手动工厂到依赖注入的自动装配
理解这种演进比记住23种模式的结构更重要。当你能判断”这个问题不需要设计模式”的时候,才是真正掌握了设计模式。
相关技术栈
Java 21 (LTS)
Spring Boot 3.4.x
Spring Security
Spring MVC Interceptor
推荐阅读
《设计模式:可复用面向对象软件的基础》— Erich Gamma et al.
《重构》— Martin Fowler
《架构整洁之道》— Robert C. Martin