1. 策略模式在多实体操作中的实战应用
在Java企业级开发中,我们经常会遇到需要处理多种相似但又不完全相同实体对象的场景。比如电商系统中同时存在商品、促销活动、会员卡等多种业务实体,它们都需要进行CRUD操作,但各自的业务规则和数据处理逻辑又存在差异。传统做法会导致大量重复代码或者复杂的条件判断,这正是策略模式大显身手的地方。
1.1 为什么选择策略模式
策略模式的核心在于定义一系列算法(策略),将它们封装成独立的类,使得它们可以相互替换。这种模式特别适合以下场景:
- 系统需要在多种算法中选择一种
- 需要避免暴露复杂的、与算法相关的数据结构
- 一个类定义了多种行为,并且这些行为以多个条件语句的形式出现
在我们的案例中,不同实体(BannerInfo、Product、MallProduct)的操作逻辑就是不同的"策略",通过策略模式我们可以:
- 消除重复的if-else或switch-case语句
- 将变化的部分(各实体的特殊处理逻辑)与不变的部分(通用操作流程)分离
- 提高代码的可测试性,每个策略可以单独测试
- 符合开闭原则,新增实体类型时只需添加新策略而不用修改现有代码
1.2 策略模式实现的关键组件
1.2.1 策略接口设计
java复制public interface EntityOperation<T> {
// 获取实体类型名称
String getEntityType();
// 获取实体标识符
Integer getIdentifier(T entity);
// 获取激活标志
String getActivationFlag(T entity);
// 获取控制标志
String getControlFlag(T entity);
// 设置关联关系
void setupRelations(RelationRecord record, T entity);
// 清理旧数据
void cleanupOldData(DataService service, Integer identifier);
// 查询数据
List<RelationRecord> fetchData(DataService service, Integer identifier);
}
接口设计要点:
- 使用泛型
支持不同类型的实体 - 方法命名清晰表达意图
- 包含实体操作所需的全部行为契约
- 参数设计考虑实际业务需求(如需要DataService进行数据访问)
1.2.2 具体策略实现
以Banner操作为例:
java复制@Component
public class BannerOperation implements EntityOperation<BannerInfo> {
@Override
public String getEntityType() {
return "Banner";
}
@Override
public Integer getIdentifier(BannerInfo entity) {
return entity.getRefno();
}
@Override
public String getActivationFlag(BannerInfo entity) {
return entity.getEnableNewUserTagFlag();
}
@Override
public String getControlFlag(BannerInfo entity) {
return entity.getUserTagControlFlag();
}
@Override
public void setupRelations(RelationRecord record, BannerInfo entity) {
record.setBannerRefno(entity.getRefno());
record.setProductRefno(null);
record.setMallProductRefno(null);
}
@Override
public void cleanupOldData(DataService service, Integer identifier) {
service.removeByBannerRefno(identifier);
}
@Override
public List<RelationRecord> fetchData(DataService service, Integer identifier) {
return service.retrieveByBannerRefno(identifier);
}
}
实现要点:
- 每个方法只关注当前实体类型的特定逻辑
- 方法实现简洁直接,不包含条件判断
- 依赖注入的service用于数据访问
- 符合单一职责原则
1.2.3 策略的注册与管理
java复制@Component
public class EntityOperationRegistry {
private final Map<String, EntityOperation<?>> operationMap = new ConcurrentHashMap<>();
@Autowired
public EntityOperationRegistry(List<EntityOperation<?>> operations) {
operations.forEach(op -> operationMap.put(op.getEntityType(), op));
}
@SuppressWarnings("unchecked")
public <T> EntityOperation<T> getOperation(String entityType) {
return (EntityOperation<T>) operationMap.get(entityType);
}
}
使用Spring的自动注入功能收集所有策略实现,并通过实体类型作为key建立映射关系。这种方式比静态常量更灵活,支持动态扩展。
2. 统一操作管理器的设计与实现
2.1 核心架构设计
EntityManager作为策略模式中的Context角色,负责:
- 维护对Strategy对象的引用
- 定义执行策略的接口
- 实现通用的操作流程模板
java复制@Component
public class EntityManager {
private static final Logger log = LoggerFactory.getLogger(EntityManager.class);
@Autowired
private DataService dataService;
@Autowired
private EntityOperationRegistry operationRegistry;
public <T> void saveEntityRelations(T entity, List<RelationDto> relationDtos) {
EntityOperation<T> operation = operationRegistry.getOperation(
entity.getClass().getSimpleName());
if (operation == null) {
throw new IllegalArgumentException("Unsupported entity type: "
+ entity.getClass().getName());
}
saveEntityRelations(entity, relationDtos, operation);
}
private <T> void saveEntityRelations(T entity, List<RelationDto> relationDtos,
EntityOperation<T> operation) {
// 验证前置条件
if (!validatePreconditions(entity, relationDtos, operation)) {
cleanupExistingRelations(entity, operation);
return;
}
try {
// 清理旧数据
cleanupExistingRelations(entity, operation);
// 构建新数据
List<RelationRecord> records = buildRelationRecords(entity, relationDtos, operation);
// 批量保存
if (!records.isEmpty()) {
boolean success = dataService.batchSave(records);
logOperationResult(success, records.size(), operation, entity);
}
} catch (Exception e) {
log.error("操作失败 - 实体类型: {}, 标识符: {}",
operation.getEntityType(), operation.getIdentifier(entity), e);
throw new OperationFailedException("保存关联数据失败", e);
}
}
// 其他方法保持不变...
}
2.2 模板方法的应用
EntityManager中的操作流程实际上是一个模板方法模式的应用:
- 验证前置条件:检查输入参数和实体状态
- 清理旧数据:根据策略清理现有关联数据
- 构建新数据:将DTO转换为持久化对象
- 执行保存操作:批量保存新数据
- 处理结果:记录日志或抛出异常
这种设计将不变的操作流程与变化的策略实现分离,既保证了流程的一致性,又支持不同实体的特殊处理。
2.3 异常处理设计
统一的异常处理策略:
- 参数校验失败:清理现有数据并静默返回
- 业务操作失败:记录详细错误日志并抛出业务异常
- 系统异常:捕获并转换为业务异常抛出
java复制public class OperationFailedException extends RuntimeException {
private final String entityType;
private final Integer identifier;
public OperationFailedException(String message, String entityType,
Integer identifier, Throwable cause) {
super(message, cause);
this.entityType = entityType;
this.identifier = identifier;
}
// getters...
}
自定义异常包含实体类型和标识符信息,便于问题追踪和定位。
3. 高级应用与性能优化
3.1 策略模式的组合使用
在某些复杂场景下,可以组合使用多种设计模式:
3.1.1 策略工厂模式
java复制public interface OperationFactory {
<T> EntityOperation<T> createOperation(Class<T> entityType);
}
@Component
public class DefaultOperationFactory implements OperationFactory {
private final ApplicationContext applicationContext;
public DefaultOperationFactory(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
@SuppressWarnings("unchecked")
public <T> EntityOperation<T> createOperation(Class<T> entityType) {
String beanName = entityType.getSimpleName() + "Operation";
return (EntityOperation<T>) applicationContext.getBean(beanName);
}
}
3.1.2 策略缓存优化
java复制@Component
public class CachedOperationFactory implements OperationFactory {
private final OperationFactory delegate;
private final Map<Class<?>, EntityOperation<?>> cache = new ConcurrentHashMap<>();
public CachedOperationFactory(OperationFactory delegate) {
this.delegate = delegate;
}
@Override
public <T> EntityOperation<T> createOperation(Class<T> entityType) {
return (EntityOperation<T>) cache.computeIfAbsent(entityType,
key -> delegate.createOperation(entityType));
}
}
3.2 性能优化技巧
- 策略对象复用:策略对象通常是无状态的,可以设计为单例
- 缓存策略实例:使用缓存减少对象创建开销
- 并行处理:对于批量操作可以使用并行流
java复制public <T> void batchSaveEntities(List<T> entities, List<RelationDto> relationDtos) {
entities.parallelStream().forEach(entity -> {
EntityOperation<T> operation = operationRegistry.getOperation(
entity.getClass().getSimpleName());
saveEntityRelations(entity, relationDtos, operation);
});
}
3.3 与Spring框架的深度集成
3.3.1 条件化策略注册
java复制public interface EntityOperation<T> extends Ordered {
// 接口方法...
default boolean supports(Class<?> entityType) {
return true;
}
@Override
default int getOrder() {
return 0;
}
}
@Component
public class SmartOperationRegistry {
private final List<EntityOperation<?>> operations;
@Autowired
public SmartOperationRegistry(List<EntityOperation<?>> operations) {
this.operations = operations.stream()
.sorted(Comparator.comparingInt(EntityOperation::getOrder))
.collect(Collectors.toList());
}
@SuppressWarnings("unchecked")
public <T> EntityOperation<T> getOperation(T entity) {
return (EntityOperation<T>) operations.stream()
.filter(op -> op.supports(entity.getClass()))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(
"No operation found for " + entity.getClass()));
}
}
3.3.2 基于注解的策略发现
java复制@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface EntityOperationFor {
Class<?> value();
}
@EntityOperationFor(BannerInfo.class)
public class BannerOperation implements EntityOperation<BannerInfo> {
// 实现...
}
@Component
public class AnnotatedOperationRegistry {
private final Map<Class<?>, EntityOperation<?>> operationMap = new ConcurrentHashMap<>();
@Autowired
public AnnotatedOperationRegistry(ApplicationContext context) {
Map<String, Object> beans = context.getBeansWithAnnotation(EntityOperationFor.class);
beans.forEach((name, bean) -> {
EntityOperationFor annotation = bean.getClass().getAnnotation(EntityOperationFor.class);
operationMap.put(annotation.value(), (EntityOperation<?>) bean);
});
}
@SuppressWarnings("unchecked")
public <T> EntityOperation<T> getOperation(Class<T> entityType) {
return (EntityOperation<T>) operationMap.get(entityType);
}
}
4. 实战经验与避坑指南
4.1 常见问题与解决方案
4.1.1 策略膨胀问题
随着业务发展,策略类可能会越来越多。解决方案:
-
按模块分包:将相关策略放在同一包下
code复制com.example.operations.banner com.example.operations.product com.example.operations.member -
使用组合模式:将简单策略组合成复杂策略
java复制public class CompositeOperation<T> implements EntityOperation<T> { private final List<EntityOperation<T>> delegates; public CompositeOperation(List<EntityOperation<T>> delegates) { this.delegates = delegates; } @Override public void cleanupOldData(DataService service, Integer identifier) { delegates.forEach(op -> op.cleanupOldData(service, identifier)); } // 其他方法... } -
动态策略生成:对于简单场景可以使用动态代理
4.1.2 策略选择性能问题
当有大量策略时,线性查找可能成为性能瓶颈。优化方案:
- 使用Map缓存:以实体类型为key建立快速查找
- 使用索引:为策略添加优先级或分类标签
- 预编译策略选择器:使用注解处理器生成高效的选择代码
4.2 测试策略
4.2.1 单元测试
每个策略类应该有自己的测试类:
java复制class BannerOperationTest {
private BannerOperation operation;
private DataService dataService;
@BeforeEach
void setUp() {
operation = new BannerOperation();
dataService = mock(DataService.class);
}
@Test
void shouldReturnCorrectEntityType() {
assertEquals("Banner", operation.getEntityType());
}
@Test
void shouldSetupRelationsCorrectly() {
BannerInfo banner = new BannerInfo();
banner.setRefno(123);
RelationRecord record = new RelationRecord();
operation.setupRelations(record, banner);
assertEquals(123, record.getBannerRefno());
assertNull(record.getProductRefno());
assertNull(record.getMallProductRefno());
}
// 其他测试方法...
}
4.2.2 集成测试
测试策略与管理器的协作:
java复制@SpringBootTest
class EntityManagerIntegrationTest {
@Autowired
private EntityManager entityManager;
@Autowired
private DataService dataService;
@Test
void shouldSaveBannerRelations() {
BannerInfo banner = new BannerInfo();
banner.setRefno(1);
banner.setEnableNewUserTagFlag("Y");
banner.setUserTagControlFlag("PARTIAL");
List<RelationDto> relations = List.of(
new RelationDto("tag1", "value1"),
new RelationDto("tag2", "value2")
);
entityManager.saveEntityRelations(banner, relations);
verify(dataService, times(1)).batchSave(anyList());
}
}
4.2.3 性能测试
java复制@SpringBootTest
@ActiveProfiles("test")
class EntityOperationPerformanceTest {
@Autowired
private EntityManager entityManager;
@Test
void performanceTest() {
int iterations = 1000;
BannerInfo banner = createTestBanner();
List<RelationDto> relations = createTestRelations();
long start = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
entityManager.saveEntityRelations(banner, relations);
}
long duration = System.currentTimeMillis() - start;
assertTrue(duration < 1000,
"1000次操作应在1秒内完成,实际耗时: " + duration + "ms");
}
}
4.3 实际项目中的经验教训
-
策略粒度的把控:
- 策略划分过粗会导致策略内部仍然存在条件判断
- 策略划分过细会导致类爆炸
- 经验法则:当发现策略类中出现if-else判断时,考虑进一步拆分策略
-
上下文信息的传递:
- 避免在策略接口中设计过多参数
- 对于复杂上下文,可以引入Context对象封装
java复制public class OperationContext<T> { private T entity; private List<RelationDto> relationDtos; private User operator; private OperationType operationType; // getters/setters... } public interface EntityOperation<T> { void execute(OperationContext<T> context); } -
与领域模型的结合:
- 策略模式与领域驱动设计(DDD)结合良好
- 可以将策略实现为领域服务
- 使用领域事件通知策略执行结果
-
文档与注释:
- 为每个策略类添加文档说明其适用场景
- 在接口方法上使用JavaDoc详细说明契约
- 记录策略之间的交互关系
5. 扩展应用场景
策略模式的应用不仅限于实体操作,还可以应用于:
5.1 多支付渠道集成
java复制public interface PaymentStrategy {
PaymentResult pay(PaymentRequest request);
boolean supports(PaymentType type);
}
@Service
public class PaymentService {
private final List<PaymentStrategy> strategies;
public PaymentService(List<PaymentStrategy> strategies) {
this.strategies = strategies;
}
public PaymentResult processPayment(PaymentRequest request) {
return strategies.stream()
.filter(s -> s.supports(request.getType()))
.findFirst()
.orElseThrow(() -> new UnsupportedPaymentTypeException(
"Unsupported payment type: " + request.getType()))
.pay(request);
}
}
5.2 多通知方式发送
java复制public interface NotificationStrategy {
NotificationResult send(Notification notification);
boolean supports(NotificationType type);
}
@Service
public class NotificationDispatcher {
private final List<NotificationStrategy> strategies;
public NotificationDispatcher(List<NotificationStrategy> strategies) {
this.strategies = strategies;
}
public NotificationResult dispatch(Notification notification) {
return strategies.stream()
.filter(s -> s.supports(notification.getType()))
.findFirst()
.orElseThrow(() -> new UnsupportedNotificationTypeException(
"Unsupported notification type: " + notification.getType()))
.send(notification);
}
}
5.3 多数据源访问
java复制public interface DataSourceStrategy {
List<DataRecord> query(QueryCriteria criteria);
boolean supports(DataSourceType type);
}
@Service
public class DataQueryService {
private final List<DataSourceStrategy> strategies;
public DataQueryService(List<DataSourceStrategy> strategies) {
this.strategies = strategies;
}
public List<DataRecord> query(DataSourceType sourceType, QueryCriteria criteria) {
return strategies.stream()
.filter(s -> s.supports(sourceType))
.findFirst()
.orElseThrow(() -> new UnsupportedDataSourceException(
"Unsupported data source: " + sourceType))
.query(criteria);
}
}
5.4 业务规则引擎
java复制public interface BusinessRule {
boolean evaluate(Context context);
void execute(Context context);
int getPriority();
String getRuleName();
}
@Service
public class RuleEngine {
private final List<BusinessRule> rules;
public RuleEngine(List<BusinessRule> rules) {
this.rules = rules.stream()
.sorted(Comparator.comparingInt(BusinessRule::getPriority))
.collect(Collectors.toList());
}
public void executeRules(Context context) {
rules.stream()
.filter(rule -> rule.evaluate(context))
.forEach(rule -> {
log.debug("Executing rule: {}", rule.getRuleName());
rule.execute(context);
});
}
}
6. 与其他设计模式的协同
策略模式常与其他设计模式结合使用,形成更强大的解决方案:
6.1 策略模式+工厂模式
java复制public interface StrategyFactory {
EntityOperation<?> createStrategy(String entityType);
}
public class DefaultStrategyFactory implements StrategyFactory {
private final ApplicationContext context;
public DefaultStrategyFactory(ApplicationContext context) {
this.context = context;
}
@Override
public EntityOperation<?> createStrategy(String entityType) {
String beanName = entityType.toLowerCase() + "Operation";
return context.getBean(beanName, EntityOperation.class);
}
}
6.2 策略模式+模板方法模式
java复制public abstract class AbstractEntityOperation<T> implements EntityOperation<T> {
@Override
public final void cleanupOldData(DataService service, Integer identifier) {
log.info("开始清理{}的旧数据,标识符: {}", getEntityType(), identifier);
long start = System.currentTimeMillis();
doCleanup(service, identifier);
long duration = System.currentTimeMillis() - start;
log.info("清理完成,耗时: {}ms", duration);
}
protected abstract void doCleanup(DataService service, Integer identifier);
}
6.3 策略模式+装饰器模式
java复制public class LoggingOperationDecorator<T> implements EntityOperation<T> {
private final EntityOperation<T> delegate;
public LoggingOperationDecorator(EntityOperation<T> delegate) {
this.delegate = delegate;
}
@Override
public List<RelationRecord> fetchData(DataService service, Integer identifier) {
log.info("开始获取{}数据,标识符: {}", delegate.getEntityType(), identifier);
long start = System.currentTimeMillis();
List<RelationRecord> result = delegate.fetchData(service, identifier);
long duration = System.currentTimeMillis() - start;
log.info("获取数据完成,记录数: {},耗时: {}ms",
result.size(), duration);
return result;
}
// 其他方法...
}
6.4 策略模式+责任链模式
java复制public class ChainedOperation<T> implements EntityOperation<T> {
private final List<EntityOperation<T>> chain;
public ChainedOperation(List<EntityOperation<T>> chain) {
this.chain = chain;
}
@Override
public void cleanupOldData(DataService service, Integer identifier) {
for (EntityOperation<T> operation : chain) {
operation.cleanupOldData(service, identifier);
}
}
// 其他方法...
}
7. 性能考量与最佳实践
7.1 策略对象的创建与管理
- 无状态策略:尽可能设计无状态的策略类,可以共享实例
- 对象池:对有状态的策略考虑使用对象池
- 延迟加载:对初始化成本高的策略实现延迟加载
7.2 并发考虑
- 线程安全:确保策略实现是线程安全的
- 并发策略选择:使用ConcurrentHashMap等并发集合管理策略映射
- 避免锁竞争:减少策略执行过程中的同步块
7.3 内存优化
- 轻量级策略:保持策略类精简,避免持有大对象
- Flyweight模式:共享策略间的公共部分
- 及时清理:对动态生成的策略建立清理机制
7.4 监控与调优
- 策略执行统计:记录各策略的执行次数和耗时
- 热点策略识别:找出高频执行的策略进行特别优化
- 内存占用监控:关注策略对象的内存占用情况
java复制public class MonitoredOperation<T> implements EntityOperation<T> {
private final EntityOperation<T> delegate;
private final Counter counter;
private final Timer timer;
public MonitoredOperation(EntityOperation<T> delegate, MeterRegistry registry) {
this.delegate = delegate;
this.counter = registry.counter("operation." + delegate.getEntityType() + ".count");
this.timer = registry.timer("operation." + delegate.getEntityType() + ".time");
}
@Override
public List<RelationRecord> fetchData(DataService service, Integer identifier) {
counter.increment();
return timer.record(() -> delegate.fetchData(service, identifier));
}
// 其他方法...
}
8. 现代化演进与未来趋势
8.1 函数式编程风格的策略模式
Java 8以后的函数式特性可以简化策略模式的实现:
java复制public class FunctionalEntityManager {
private final Map<Class<?>, Function<Object, EntityOperation<?>>> strategyMap;
public FunctionalEntityManager() {
strategyMap = new HashMap<>();
strategyMap.put(BannerInfo.class, b -> new BannerOperation());
strategyMap.put(Product.class, p -> new ProductOperation());
// 其他策略...
}
@SuppressWarnings("unchecked")
public <T> void saveEntityRelations(T entity, List<RelationDto> dtos) {
Function<Object, EntityOperation<?>> factory = strategyMap.get(entity.getClass());
if (factory == null) {
throw new IllegalArgumentException("Unsupported entity type");
}
EntityOperation<T> operation = (EntityOperation<T>) factory.apply(entity);
// 执行操作...
}
}
8.2 响应式策略模式
与Reactive编程结合:
java复制public interface ReactiveEntityOperation<T> {
Mono<Void> saveEntityRelations(T entity, List<RelationDto> dtos);
Flux<RelationDto> queryEntityRelations(T entity);
// 其他方法...
}
@Service
public class ReactiveEntityManager {
private final Map<Class<?>, ReactiveEntityOperation<?>> operationMap;
@Autowired
public ReactiveEntityManager(List<ReactiveEntityOperation<?>> operations) {
operationMap = operations.stream()
.collect(Collectors.toMap(
op -> getEntityType(op.getClass()),
Function.identity()));
}
@SuppressWarnings("unchecked")
public <T> Mono<Void> saveEntityRelations(T entity, List<RelationDto> dtos) {
ReactiveEntityOperation<T> operation = (ReactiveEntityOperation<T>)
operationMap.get(entity.getClass());
if (operation == null) {
return Mono.error(new IllegalArgumentException("Unsupported entity type"));
}
return operation.saveEntityRelations(entity, dtos);
}
// 其他方法...
}
8.3 策略模式的云原生演进
在云原生环境中,策略模式可以:
- 策略即服务:将策略实现为独立的微服务
- 动态策略加载:从配置中心动态加载策略配置
- 策略版本管理:支持策略的灰度发布和回滚
- 策略弹性:为策略实现熔断和降级机制
java复制@FeignClient(name = "strategy-service")
public interface RemoteStrategyClient {
@PostMapping("/strategies/{strategyName}/execute")
StrategyResult execute(@PathVariable String strategyName,
@RequestBody StrategyRequest request);
}
@Service
public class CloudNativeStrategyManager {
private final RemoteStrategyClient strategyClient;
@Autowired
public CloudNativeStrategyManager(RemoteStrategyClient strategyClient) {
this.strategyClient = strategyClient;
}
public Mono<StrategyResult> executeStrategy(String strategyName, Object input) {
StrategyRequest request = new StrategyRequest(input);
return strategyClient.execute(strategyName, request)
.timeout(Duration.ofSeconds(5))
.onErrorResume(e -> Mono.just(
new StrategyResult("fallback", "Fallback result due to error")));
}
}
9. 总结与个人实践心得
在实际项目中应用策略模式多年,我总结了以下几点关键经验:
-
识别策略的时机:当发现自己在写"如果是A则这样做,如果是B则那样做"时,就是引入策略模式的好时机
-
接口设计原则:
- 保持策略接口精简专注
- 避免策略接口过于庞大
- 考虑使用默认方法减少实现类负担
-
性能与维护的平衡:
- 不要为了微小的性能提升牺牲代码清晰度
- 策略查找的性能通常不是瓶颈
- 清晰的策略划分带来的维护性提升更有价值
-
测试友好性:
- 每个策略应该可以独立测试
- 使用Mock策略进行集成测试
- 考虑策略的排列组合测试场景
-
文档的重要性:
- 为每个策略编写清晰的用途说明
- 记录策略之间的交互关系
- 维护策略选择流程图
-
团队共识:
- 确保团队成员理解策略模式的价值
- 建立统一的策略实现规范
- 定期评审策略划分的合理性
策略模式不是银弹,但在处理算法或行为变体时,它确实能显著提高代码的可维护性和扩展性。关键在于识别真正的"变化点",并将其优雅地封装起来。随着业务的发展,当初的设计可能需要调整,但策略模式提供的灵活性使得这种调整变得更加可控。