1. 为什么我们需要动态切换外卖平台API?
在开发外卖聚合系统时,我遇到了一个典型问题:每个外卖平台(美团、饿了么、滴滴外卖等)的API接口规范各不相同。最初我采用了最直接的if-else方式处理:
java复制if (platform.equals("meituan")) {
// 调用美团API逻辑
} else if (platform.equals("eleme")) {
// 调用饿了么API逻辑
}
这种写法在接入3-4个平台时还能勉强维护,但当平台数量增加到10+时,代码就变成了难以维护的"面条代码"。更糟糕的是,每次新增平台都需要修改核心业务类,违反了开闭原则。
2. 策略模式的核心思想解析
策略模式(Strategy Pattern)属于行为型设计模式,其核心定义是:定义一系列算法,将每个算法封装起来,并使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户端。
在外卖API调用的场景中,我们可以将不同平台的API调用逻辑抽象为统一的策略接口:
java复制public interface TakeawayApiStrategy {
OrderResult createOrder(CreateOrderRequest request);
OrderStatusResult queryOrderStatus(String orderId);
CancelOrderResult cancelOrder(String orderId);
// 其他通用外卖操作...
}
每个具体平台实现这个接口:
java复制public class MeituanApiStrategy implements TakeawayApiStrategy {
@Override
public OrderResult createOrder(CreateOrderRequest request) {
// 美团特有的签名生成逻辑
String sign = MeituanSignUtil.generate(request);
// 美团特有的参数转换
Map<String, String> params = convertToMeituanParams(request);
// 调用美团HTTP接口
return httpClient.post(MEITUAN_URL, params);
}
// 其他方法实现...
}
3. 动态策略选择的四种实现方案
3.1 基于Spring的注解驱动方案
对于Spring项目,可以结合@Component和@Service注解实现策略自动注册:
java复制@Service
public class TakeawayStrategyFactory {
private final Map<String, TakeawayApiStrategy> strategyMap = new ConcurrentHashMap<>();
@Autowired
public TakeawayStrategyFactory(List<TakeawayApiStrategy> strategies) {
for (TakeawayApiStrategy strategy : strategies) {
// 通过注解获取平台标识
Platform platform = strategy.getClass()
.getAnnotation(Platform.class);
strategyMap.put(platform.value(), strategy);
}
}
public TakeawayApiStrategy getStrategy(String platform) {
return strategyMap.get(platform);
}
}
使用时只需注入工厂类:
java复制@RestController
public class OrderController {
@Autowired
private TakeawayStrategyFactory strategyFactory;
@PostMapping("/order")
public OrderResult createOrder(@RequestBody OrderRequest request) {
TakeawayApiStrategy strategy = strategyFactory
.getStrategy(request.getPlatform());
return strategy.createOrder(request);
}
}
3.2 基于枚举的轻量级实现
对于简单场景,可以使用枚举策略模式:
java复制public enum TakeawayStrategyEnum {
MEITUAN {
@Override
public OrderResult createOrder(CreateOrderRequest request) {
// 美团实现
}
},
ELEME {
@Override
public OrderResult createOrder(CreateOrderRequest request) {
// 饿了么实现
}
};
public abstract OrderResult createOrder(CreateOrderRequest request);
}
3.3 动态配置的热更新方案
在需要支持动态添加新平台的场景下,可以采用配置中心+反射的方案:
java复制public class DynamicStrategyLoader {
private static final Map<String, Class<?>> STRATEGY_CLASSES = new ConcurrentHashMap<>();
// 从配置中心加载策略类
public static void loadStrategies(ConfigCenter config) {
config.getStrategyConfigs().forEach((name, className) -> {
try {
Class<?> clazz = Class.forName(className);
STRATEGY_CLASSES.put(name, clazz);
} catch (ClassNotFoundException e) {
log.error("策略类加载失败", e);
}
});
}
public static TakeawayApiStrategy getStrategy(String platform) {
Class<?> clazz = STRATEGY_CLASSES.get(platform);
return (TakeawayApiStrategy) clazz.newInstance();
}
}
3.4 策略模式与模板方法结合
对于各平台有部分相同逻辑的情况,可以结合模板方法模式:
java复制public abstract class AbstractTakeawayStrategy implements TakeawayApiStrategy {
// 公共的签名验证方法
protected final boolean verifySign(ApiRequest request) {
// 通用签名验证逻辑
}
// 抽象方法由子类实现
protected abstract String generatePlatformSign(ApiRequest request);
@Override
public OrderResult createOrder(CreateOrderRequest request) {
// 1. 参数校验(公共逻辑)
validateParams(request);
// 2. 生成平台特有签名(由子类实现)
String sign = generatePlatformSign(request);
// 3. 调用平台接口(由子类实现)
return callPlatformApi(request, sign);
}
}
4. 生产环境中的实战经验
4.1 策略的线程安全性处理
策略对象通常是无状态的,可以被多线程共享。但如果有平台特定的连接池或缓存,需要特别注意:
java复制public class MeituanApiStrategy implements TakeawayApiStrategy {
// 美团专用的HTTP连接池
private final HttpClient meituanHttpClient;
public MeituanApiStrategy() {
this.meituanHttpClient = createMeituanClient();
}
private HttpClient createMeituanClient() {
return HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.sslContext(meituanSslContext()) // 美团特有SSL配置
.build();
}
}
4.2 策略的性能优化技巧
- 策略对象缓存:避免频繁创建策略对象
java复制public class StrategyCache {
private static final Map<String, TakeawayApiStrategy> CACHE = new ConcurrentHashMap<>();
public static TakeawayApiStrategy getStrategy(String platform) {
return CACHE.computeIfAbsent(platform, p -> {
// 初始化策略
return initStrategy(p);
});
}
}
- 异步策略执行:对于耗时操作使用异步策略
java复制public interface AsyncTakeawayStrategy {
CompletableFuture<OrderResult> createOrderAsync(CreateOrderRequest request);
}
4.3 监控与降级策略
为每个策略添加监控指标:
java复制public class MonitoredStrategy implements TakeawayApiStrategy {
private final TakeawayApiStrategy delegate;
private final MeterRegistry meterRegistry;
@Override
public OrderResult createOrder(CreateOrderRequest request) {
Timer.Sample sample = Timer.start();
try {
OrderResult result = delegate.createOrder(request);
sample.stop(meterRegistry.timer("takeaway.api.time", "platform", request.getPlatform()));
return result;
} catch (Exception e) {
sample.stop(meterRegistry.timer("takeaway.api.error", "platform", request.getPlatform()));
throw e;
}
}
}
降级策略示例:
java复制public class CircuitBreakerStrategy implements TakeawayApiStrategy {
private final CircuitBreaker circuitBreaker;
private final TakeawayApiStrategy delegate;
@Override
public OrderResult createOrder(CreateOrderRequest request) {
return circuitBreaker.executeSupplier(() -> delegate.createOrder(request));
}
}
5. 策略模式在外卖系统中的扩展应用
5.1 多级策略组合
对于大型平台,可以组合多种策略:
java复制public class CompositeStrategy implements TakeawayApiStrategy {
private final List<TakeawayApiStrategy> strategies;
@Override
public OrderResult createOrder(CreateOrderRequest request) {
for (TakeawayApiStrategy strategy : strategies) {
try {
return strategy.createOrder(request);
} catch (PlatformNotAvailableException e) {
continue; // 尝试下一个策略
}
}
throw new AllStrategiesFailedException();
}
}
5.2 策略的自动化测试方案
使用Mock策略进行测试:
java复制public class TestTakeawayStrategy implements TakeawayApiStrategy {
@Override
public OrderResult createOrder(CreateOrderRequest request) {
// 返回预设的测试数据
return new OrderResult("test_order_id", "SUCCESS");
}
}
策略的契约测试:
java复制public abstract class TakeawayStrategyContractTest {
protected abstract TakeawayApiStrategy createStrategy();
@Test
public void shouldReturnOrderIdWhenCreateSuccess() {
TakeawayApiStrategy strategy = createStrategy();
OrderResult result = strategy.createOrder(testRequest());
assertNotNull(result.getOrderId());
}
}
5.3 策略的配置化管理
通过数据库管理策略配置:
sql复制CREATE TABLE takeaway_strategy_config (
platform VARCHAR(50) PRIMARY KEY,
strategy_class VARCHAR(200) NOT NULL,
enabled BOOLEAN DEFAULT TRUE,
priority INT DEFAULT 0
);
动态加载配置:
java复制@Scheduled(fixedRate = 60000) // 每分钟刷新一次
public void refreshStrategies() {
List<StrategyConfig> configs = strategyConfigRepository.findAll();
strategyFactory.refresh(configs);
}
6. 策略模式与其他模式的协作
6.1 策略+工厂方法模式
java复制public interface TakeawayStrategyFactory {
TakeawayApiStrategy createStrategy(String platform);
}
public class DefaultTakeawayStrategyFactory implements TakeawayStrategyFactory {
@Override
public TakeawayApiStrategy createStrategy(String platform) {
switch (platform) {
case "meituan": return new MeituanStrategy();
case "eleme": return new ElemeStrategy();
default: throw new IllegalArgumentException();
}
}
}
6.2 策略+装饰器模式
java复制public class LoggingStrategyDecorator implements TakeawayApiStrategy {
private final TakeawayApiStrategy delegate;
@Override
public OrderResult createOrder(CreateOrderRequest request) {
log.info("调用平台API: {}", request.getPlatform());
try {
OrderResult result = delegate.createOrder(request);
log.info("调用成功: {}", result);
return result;
} catch (Exception e) {
log.error("调用失败", e);
throw e;
}
}
}
6.3 策略+责任链模式
java复制public class ChainOfStrategies implements TakeawayApiStrategy {
private final List<TakeawayApiStrategy> chain;
@Override
public OrderResult createOrder(CreateOrderRequest request) {
for (TakeawayApiStrategy strategy : chain) {
if (strategy.supports(request)) {
return strategy.createOrder(request);
}
}
throw new UnsupportedPlatformException();
}
}
7. 不同语言下的策略模式实现对比
7.1 Java实现要点
java复制// 使用函数式接口简化策略定义
@FunctionalInterface
public interface TakeawayStrategy {
OrderResult createOrder(CreateOrderRequest request);
}
// 使用Lambda表达式创建策略
TakeawayStrategy meituanStrategy = request -> {
// 美团实现逻辑
};
7.2 Python的实现方式
python复制from abc import ABC, abstractmethod
class TakeawayStrategy(ABC):
@abstractmethod
def create_order(self, request):
pass
class MeituanStrategy(TakeawayStrategy):
def create_order(self, request):
# 美团实现
return OrderResult(...)
# 使用策略
strategy = MeituanStrategy()
result = strategy.create_order(request)
7.3 Go语言的实现
go复制type TakeawayStrategy interface {
CreateOrder(request CreateOrderRequest) (OrderResult, error)
}
type MeituanStrategy struct{}
func (s *MeituanStrategy) CreateOrder(request CreateOrderRequest) (OrderResult, error) {
// 美团实现
}
// 使用策略
var strategy TakeawayStrategy = &MeituanStrategy{}
result, err := strategy.CreateOrder(request)
7.4 JavaScript的实现
javascript复制class TakeawayStrategy {
createOrder(request) {
throw new Error('必须实现createOrder方法');
}
}
class MeituanStrategy extends TakeawayStrategy {
createOrder(request) {
// 美团实现
return fetchMeituanApi(request);
}
}
// 使用策略
const strategy = new MeituanStrategy();
const result = await strategy.createOrder(request);
8. 策略模式在外卖系统外的应用场景
8.1 支付网关集成
不同支付渠道(支付宝、微信、银联)的集成是策略模式的经典应用场景。
8.2 物流配送计算
针对不同物流公司(顺丰、中通、京东物流)的运费计算策略。
8.3 促销活动引擎
各种促销活动(满减、折扣、赠品)可以看作是不同的策略。
8.4 数据分析导出
支持导出Excel、CSV、PDF等不同格式的策略实现。
9. 策略模式的局限性及应对方案
9.1 策略类膨胀问题
当策略过多时,会导致类数量激增。解决方案:
- 使用组合策略
- 按功能维度拆分策略模块
- 采用动态代码生成技术
9.2 策略选择逻辑复杂化
当策略选择条件过于复杂时:
java复制// 使用规则引擎代替复杂条件判断
public class RuleEngineStrategySelector {
private final RuleEngine engine;
public TakeawayApiStrategy selectStrategy(OrderRequest request) {
Rules rules = engine.evaluate(request);
return rules.getStrategy();
}
}
9.3 策略间的共享状态问题
当策略需要共享状态时:
java复制public class ContextAwareStrategy implements TakeawayApiStrategy {
private final TakeawayContext context;
private final TakeawayApiStrategy delegate;
@Override
public OrderResult createOrder(CreateOrderRequest request) {
// 使用共享上下文
context.setCurrentRequest(request);
return delegate.createOrder(request);
}
}
10. 从策略模式到领域驱动设计
在复杂业务系统中,策略模式可以演进为领域模型中的策略对象:
java复制public class TakeawayPlatform {
private PlatformId id;
private ApiConfig apiConfig;
private TakeawayStrategy strategy;
public OrderResult createOrder(CreateOrderRequest request) {
return strategy.createOrder(request.withConfig(apiConfig));
}
}
结合领域事件:
java复制public class TakeawayOrderService {
@Transactional
public OrderResult createOrder(CreateOrderRequest request) {
TakeawayPlatform platform = platformRepository.findById(request.getPlatformId());
OrderResult result = platform.createOrder(request);
eventPublisher.publish(new OrderCreatedEvent(result));
return result;
}
}
