1. 策略模式入门:告别if-else地狱的利器
作为一名在Java领域摸爬滚打多年的开发者,我见过太多因为滥用if-else而变得难以维护的代码。今天我要分享的策略模式,正是解决这个痛点的绝佳方案。它不是什么高深的理论,而是一个能立即提升你代码质量的实用工具。
策略模式的核心思想很简单:把会变化的算法部分抽取出来,封装成独立的策略类。这样当需求变更时,你只需要新增或修改策略类,而不用动主流程代码。想象一下,如果你的支付系统有10种支付方式,用传统if-else写法会是什么样子?每次新增支付方式都要修改主流程,风险高、测试难、维护成本大。
提示:策略模式特别适合处理那些"同一问题有多种解法"的场景,比如支付方式、折扣策略、排序算法等。
2. 环境准备与项目搭建
2.1 开发环境配置
工欲善其事,必先利其器。在开始编码前,我们需要确保开发环境准备妥当:
-
JDK选择:推荐使用JDK 17(LTS版本),它提供了更好的性能和语言特性支持。可以通过
java -version命令检查当前版本。 -
IDE选择:
- IntelliJ IDEA(社区版即可)
- 或者VS Code + Java插件包
-
构建工具:我们将使用Maven管理项目依赖。在IDEA中创建新项目时,选择"Maven"类型,并勾选"Create from archetype",然后选择
maven-archetype-quickstart。
2.2 常见环境问题排查
在实际教学中,我发现新手常遇到以下问题:
-
版本不匹配:
xml复制<!-- 确保pom.xml中的JDK版本与实际一致 --> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties> -
依赖问题:
- 如果遇到
NoClassDefFoundError,先执行mvn clean install - 确保IDEA中正确配置了Maven路径和仓库位置
- 如果遇到
-
运行配置:
- 右键Main类 → Run 'Main.main()'
- 或者使用命令行:
mvn compile exec:java -Dexec.mainClass="com.example.Main"
3. 策略模式实战:支付系统案例
3.1 基础实现三步走
让我们通过一个电商支付系统的案例,一步步实现策略模式。
第一步:定义策略接口
java复制public interface PaymentStrategy {
void processPayment(double amount);
String getPaymentMethod();
}
这个接口定义了所有支付策略必须实现的两个方法:处理支付和获取支付方式名称。
第二步:实现具体策略
java复制// 微信支付实现
public class WeChatPayment implements PaymentStrategy {
@Override
public void processPayment(double amount) {
System.out.printf("Processing WeChat payment of ¥%.2f%n", amount);
// 实际项目中这里会调用微信支付API
}
@Override
public String getPaymentMethod() {
return "wechat";
}
}
// 支付宝支付实现
public class AlipayPayment implements PaymentStrategy {
@Override
public void processPayment(double amount) {
System.out.printf("Processing Alipay payment of ¥%.2f%n", amount);
// 调用支付宝支付API
}
@Override
public String getPaymentMethod() {
return "alipay";
}
}
第三步:创建支付上下文
java复制import java.util.HashMap;
import java.util.Map;
public class PaymentContext {
private final Map<String, PaymentStrategy> strategies = new HashMap<>();
public PaymentContext() {
registerStrategy(new WeChatPayment());
registerStrategy(new AlipayPayment());
}
public void registerStrategy(PaymentStrategy strategy) {
strategies.put(strategy.getPaymentMethod(), strategy);
}
public void executePayment(String method, double amount) {
PaymentStrategy strategy = strategies.get(method);
if (strategy == null) {
throw new IllegalArgumentException("Unsupported payment method: " + method);
}
strategy.processPayment(amount);
}
}
3.2 测试我们的实现
java复制public class Main {
public static void main(String[] args) {
PaymentContext context = new PaymentContext();
// 测试微信支付
context.executePayment("wechat", 100.50);
// 测试支付宝支付
context.executePayment("alipay", 200.75);
// 测试不支持的支付方式
try {
context.executePayment("paypal", 50.00);
} catch (IllegalArgumentException e) {
System.out.println("Error: " + e.getMessage());
}
}
}
运行结果应该如下:
code复制Processing WeChat payment of ¥100.50
Processing Alipay payment of ¥200.75
Error: Unsupported payment method: paypal
4. 进阶技巧与最佳实践
4.1 策略模式的Spring Boot集成
在实际企业项目中,我们通常会使用Spring框架。下面是策略模式与Spring Boot的集成方式:
java复制// 1. 定义策略接口
public interface PaymentStrategy {
String getType();
void processPayment(double amount);
}
// 2. 实现策略(加上@Service注解)
@Service
public class WeChatPayment implements PaymentStrategy {
@Override
public String getType() {
return "wechat";
}
@Override
public void processPayment(double amount) {
// 实现细节
}
}
// 3. 创建策略工厂
@Service
public class PaymentStrategyFactory {
private final Map<String, PaymentStrategy> strategies;
@Autowired
public PaymentStrategyFactory(List<PaymentStrategy> strategyList) {
strategies = strategyList.stream()
.collect(Collectors.toMap(PaymentStrategy::getType, Function.identity()));
}
public PaymentStrategy getStrategy(String type) {
PaymentStrategy strategy = strategies.get(type);
if (strategy == null) {
throw new IllegalArgumentException("Unsupported payment type: " + type);
}
return strategy;
}
}
// 4. 在Controller中使用
@RestController
@RequestMapping("/payments")
public class PaymentController {
@Autowired
private PaymentStrategyFactory strategyFactory;
@PostMapping("/{type}")
public ResponseEntity<String> makePayment(
@PathVariable String type,
@RequestParam double amount) {
PaymentStrategy strategy = strategyFactory.getStrategy(type);
strategy.processPayment(amount);
return ResponseEntity.ok("Payment processed successfully");
}
}
这种实现方式的优势:
- 自动依赖注入,无需手动创建策略实例
- 新增支付方式只需添加新的@Service类,符合开闭原则
- 便于进行单元测试和Mock
4.2 策略模式与其他模式的组合
策略+工厂模式
java复制public class PaymentStrategyFactory {
public static PaymentStrategy create(String type) {
switch (type) {
case "wechat": return new WeChatPayment();
case "alipay": return new AlipayPayment();
// 可以添加更多支付方式
default: throw new IllegalArgumentException("Unknown payment type");
}
}
}
策略+注解驱动
java复制@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface PaymentType {
String value();
}
@PaymentType("wechat")
public class WeChatPayment implements PaymentStrategy {
// 实现略
}
// 在工厂类中扫描注解
public class PaymentStrategyFactory {
private final Map<String, Class<? extends PaymentStrategy>> strategies = new HashMap<>();
public PaymentStrategyFactory() {
// 使用反射扫描带有PaymentType注解的类
Reflections reflections = new Reflections("com.example.payment");
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(PaymentType.class);
for (Class<?> clazz : annotated) {
PaymentType annotation = clazz.getAnnotation(PaymentType.class);
strategies.put(annotation.value(), (Class<? extends PaymentStrategy>) clazz);
}
}
public PaymentStrategy create(String type) throws Exception {
Class<? extends PaymentStrategy> clazz = strategies.get(type);
if (clazz == null) {
throw new IllegalArgumentException("Unknown payment type");
}
return clazz.getDeclaredConstructor().newInstance();
}
}
5. 策略模式的深度解析
5.1 设计原则应用
策略模式完美体现了以下几个面向对象设计原则:
- 开闭原则(OCP):对扩展开放,对修改关闭。新增支付方式不需要修改现有代码。
- 单一职责原则(SRP):每个策略类只负责一种算法实现。
- 依赖倒置原则(DIP):高层模块(PaymentContext)依赖抽象(PaymentStrategy),不依赖具体实现。
5.2 性能考量
虽然策略模式带来了很多好处,但在性能敏感的场景下需要考虑:
- 对象创建开销:频繁创建策略实例会影响性能,可以考虑使用对象池或享元模式。
- 内存占用:大量策略类会增加内存消耗,需要权衡。
- 缓存策略:对于计算密集型策略,可以引入缓存机制。
5.3 与状态模式的对比
很多初学者容易混淆策略模式和状态模式,它们的区别如下:
| 特性 | 策略模式 | 状态模式 |
|---|---|---|
| 目的 | 封装可互换的算法 | 封装与状态相关的行为 |
| 切换时机 | 客户端主动选择 | 由状态转换自动触发 |
| 典型应用 | 支付方式、排序算法、折扣策略 | 订单状态、工作流、游戏角色状态 |
6. 实战中的经验与陷阱
6.1 常见问题与解决方案
-
策略膨胀问题
- 现象:策略类过多,难以管理
- 解决方案:
- 按功能分组,使用分层策略
- 引入策略组合模式
-
策略共享状态问题
- 现象:多个策略需要共享数据
- 解决方案:
- 将共享数据放入Context中
- 使用线程安全的共享对象
-
动态策略切换
- 需求:运行时根据条件改变策略
- 实现:
java复制public class DynamicPaymentContext { private PaymentStrategy strategy; public void setStrategy(PaymentStrategy strategy) { this.strategy = strategy; } public void executePayment(double amount) { strategy.processPayment(amount); } }
6.2 性能优化技巧
-
策略缓存:对于创建成本高的策略,可以缓存实例
java复制public class CachedStrategyFactory { private final Map<String, PaymentStrategy> cache = new ConcurrentHashMap<>(); public PaymentStrategy getStrategy(String type) { return cache.computeIfAbsent(type, this::createStrategy); } private PaymentStrategy createStrategy(String type) { // 创建策略的逻辑 } } -
无状态策略:尽可能设计无状态的策略类,便于复用
java复制// 无状态策略示例 public class DiscountStrategy { public double applyDiscount(Order order) { // 只使用order中的数据,不维护自身状态 } } -
并行处理:适合处理批量任务的策略可以并行化
java复制public class ParallelProcessor { private final ProcessingStrategy strategy; public void processAll(List<Item> items) { items.parallelStream().forEach(strategy::process); } }
7. 实际项目中的应用场景
7.1 电商系统中的典型应用
- 支付系统(我们演示的案例)
- 促销折扣系统
- 满减策略
- 折扣券策略
- 会员折扣策略
- 物流运费计算
- 按重量计费
- 按体积计费
- 特殊商品计费
7.2 金融系统中的应用
- 风险评估模型
- 不同风险等级使用不同评估策略
- 利息计算
- 活期利息
- 定期利息
- 复利计算
- 交易手续费计算
- 国内交易
- 跨境交易
- 大额交易
7.3 游戏开发中的应用
- AI行为策略
- 攻击策略
- 防御策略
- 逃跑策略
- 伤害计算
- 物理伤害
- 魔法伤害
- 真实伤害
- 经验值计算
- 普通怪物
- BOSS怪物
- 任务经验
8. 测试策略模式的代码
良好的测试是保证策略模式正确实施的关键。下面是一些测试建议:
8.1 单元测试策略类
java复制public class WeChatPaymentTest {
private WeChatPayment payment;
@BeforeEach
void setUp() {
payment = new WeChatPayment();
}
@Test
void testProcessPayment() {
assertDoesNotThrow(() -> payment.processPayment(100.0));
}
@Test
void testGetPaymentMethod() {
assertEquals("wechat", payment.getPaymentMethod());
}
}
8.2 测试策略上下文
java复制public class PaymentContextTest {
private PaymentContext context;
@BeforeEach
void setUp() {
context = new PaymentContext();
}
@Test
void testExecutePaymentWithValidMethod() {
assertDoesNotThrow(() -> context.executePayment("wechat", 100.0));
}
@Test
void testExecutePaymentWithInvalidMethod() {
assertThrows(IllegalArgumentException.class,
() -> context.executePayment("invalid", 100.0));
}
@Test
void testRegisterNewStrategy() {
PaymentStrategy mockStrategy = mock(PaymentStrategy.class);
when(mockStrategy.getPaymentMethod()).thenReturn("test");
context.registerStrategy(mockStrategy);
context.executePayment("test", 50.0);
verify(mockStrategy).processPayment(50.0);
}
}
8.3 集成测试(Spring Boot)
java复制@SpringBootTest
public class PaymentIntegrationTest {
@Autowired
private PaymentStrategyFactory factory;
@Test
void testWeChatStrategyInContainer() {
PaymentStrategy strategy = factory.getStrategy("wechat");
assertNotNull(strategy);
assertTrue(strategy instanceof WeChatPayment);
}
@Test
void testAlipayStrategyInContainer() {
PaymentStrategy strategy = factory.getStrategy("alipay");
assertNotNull(strategy);
assertTrue(strategy instanceof AlipayPayment);
}
}
9. 策略模式的变体与扩展
9.1 带返回值的策略
前面的例子中策略方法都是void,实际应用中经常需要返回值:
java复制public interface ValidationStrategy {
ValidationResult validate(String input);
}
public class EmailValidation implements ValidationStrategy {
@Override
public ValidationResult validate(String email) {
boolean isValid = email.matches("^[\\w-.]+@([\\w-]+\\.)+[\\w-]{2,4}$");
return new ValidationResult(isValid, isValid ? null : "Invalid email format");
}
}
// 使用示例
ValidationStrategy strategy = new EmailValidation();
ValidationResult result = strategy.validate("test@example.com");
9.2 可配置的策略
有时策略需要一些配置参数:
java复制public interface CompressionStrategy {
byte[] compress(byte[] data, CompressionConfig config);
}
public class ZipCompression implements CompressionStrategy {
@Override
public byte[] compress(byte[] data, CompressionConfig config) {
// 使用config中的参数进行压缩
}
}
// 配置类
public class CompressionConfig {
private int level;
private String method;
// getters/setters
}
9.3 组合策略
多个策略可以组合使用,形成更复杂的行为:
java复制public class CompositeStrategy implements PaymentStrategy {
private final List<PaymentStrategy> strategies;
public CompositeStrategy(List<PaymentStrategy> strategies) {
this.strategies = strategies;
}
@Override
public void processPayment(double amount) {
for (PaymentStrategy strategy : strategies) {
strategy.processPayment(amount);
}
}
@Override
public String getPaymentMethod() {
return strategies.stream()
.map(PaymentStrategy::getPaymentMethod)
.collect(Collectors.joining("+"));
}
}
// 使用示例
PaymentStrategy composite = new CompositeStrategy(Arrays.asList(
new WeChatPayment(),
new AlipayPayment()
));
composite.processPayment(100.0); // 会同时执行两种支付
10. 策略模式在复杂系统中的应用
10.1 微服务架构中的策略模式
在微服务架构中,策略模式可以帮助我们:
-
服务路由:根据请求特征选择不同的服务实例
java复制public interface RoutingStrategy { ServiceInstance selectInstance(List<ServiceInstance> instances, Request request); } public class RoundRobinStrategy implements RoutingStrategy { private final AtomicInteger counter = new AtomicInteger(); @Override public ServiceInstance selectInstance(List<ServiceInstance> instances, Request request) { int index = counter.getAndIncrement() % instances.size(); return instances.get(index); } } -
容错策略:定义不同的失败处理方式
java复制public interface FaultToleranceStrategy { <T> T execute(Callable<T> task); } public class RetryStrategy implements FaultToleranceStrategy { private final int maxAttempts; @Override public <T> T execute(Callable<T> task) throws Exception { int attempts = 0; while (true) { try { return task.call(); } catch (Exception e) { if (++attempts >= maxAttempts) throw e; Thread.sleep(1000 * attempts); } } } }
10.2 策略模式与领域驱动设计
在DDD中,策略模式可以很好地实现领域模型中的策略概念:
-
定价策略:
java复制public interface PricingStrategy { Money calculatePrice(OrderLine line, Customer customer); } public class VolumeDiscountStrategy implements PricingStrategy { @Override public Money calculatePrice(OrderLine line, Customer customer) { int quantity = line.getQuantity(); if (quantity > 100) { return line.getBasePrice().multiply(0.8); } else if (quantity > 50) { return line.getBasePrice().multiply(0.9); } return line.getBasePrice(); } } -
发货策略:
java复制public interface ShippingStrategy { ShippingMethod selectMethod(Order order, Address destination); } public class ExpressShippingStrategy implements ShippingStrategy { @Override public ShippingMethod selectMethod(Order order, Address destination) { if (order.isUrgent() && destination.isDomestic()) { return ShippingMethod.EXPRESS; } return ShippingMethod.STANDARD; } }
10.3 策略模式与函数式编程
Java 8引入的lambda表达式让策略模式的实现更加简洁:
java复制public class FunctionalStrategy {
private final Map<String, Function<Double, String>> strategies = new HashMap<>();
public FunctionalStrategy() {
strategies.put("wechat", amount -> String.format("Processed WeChat payment: ¥%.2f", amount));
strategies.put("alipay", amount -> String.format("Processed Alipay payment: ¥%.2f", amount));
}
public String processPayment(String type, double amount) {
Function<Double, String> strategy = strategies.get(type);
if (strategy == null) {
throw new IllegalArgumentException("Unknown payment type");
}
return strategy.apply(amount);
}
}
// 使用示例
FunctionalStrategy processor = new FunctionalStrategy();
String result = processor.processPayment("wechat", 100.50);
这种实现方式特别适合简单的策略,减少了样板代码。但对于复杂策略,还是推荐使用传统的类实现方式。