引言:多实体操作抽象化的挑战
在现代软件系统中,我们经常需要处理多种不同类型的实体对象,这些实体虽然具有不同的业务含义和数据结构,但在操作层面却有着许多相似之处。例如,无论处理横幅广告、产品还是商城产品,我们都需要进行增删改查、数据转换、权限验证等操作。
本文将通过一个重构后的代码案例,展示如何运用策略模式优雅地解决多实体操作中的代码重复和复杂性管理问题。
重构前的问题
假设我们有一个系统,需要处理以下三种不同类型的实体:
- BannerInfo(横幅广告信息)
- Product(产品信息)
- MallProduct(商城产品信息)
每种实体都有:
- 唯一的标识符(refno)
- 特定的控制标志位
- 不同的数据存储方式
在传统实现中,我们可能会为每种实体编写重复的代码,或者使用大量的 if-else 或 switch 语句来判断实体类型,这会导致:
- 代码重复,维护困难
- 新增实体类型时需要修改大量代码
- 难以进行单元测试
策略模式的完美应用
核心思想
我们通过定义一个通用操作接口,为每种实体提供具体的实现策略,从而实现"同一个接口,不同实现"的目标。
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);
}
2. 具体策略实现
为每种实体类型创建具体策略:
java
@Component
public class EntityOperationHelper {
public static final EntityOperation<BannerInfo> BANNER_OPERATION =
new 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);
}
};
// Product操作策略
public static final EntityOperation<Product> PRODUCT_OPERATION =
new EntityOperation<Product>() { /* 类似实现 */ };
// MallProduct操作策略
public static final EntityOperation<MallProduct> MALL_PRODUCT_OPERATION =
new EntityOperation<MallProduct>() { /* 类似实现 */ };
}
3. 统一操作管理器
java
@Component
public class EntityManager {
private static final Logger log = LoggerFactory.getLogger(EntityManager.class);
@Autowired
private DataService dataService;
/**
* 保存实体关联数据
*/
public <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);
}
}
/**
* 查询实体关联数据
*/
public <T> List<RelationDto> queryEntityRelations(T entity, EntityOperation<T> operation) {
Integer identifier = operation.getIdentifier(entity);
if (!isQueryAllowed(entity, operation, identifier)) {
return Collections.emptyList();
}
try {
List<RelationRecord> records = operation.fetchData(dataService, identifier);
return convertToDtoList(records);
} catch (Exception e) {
log.error("查询失败 - 实体类型: {}, 标识符: {}",
operation.getEntityType(), identifier, e);
return Collections.emptyList();
}
}
// 辅助方法...
private <T> boolean validatePreconditions(T entity, List<RelationDto> dtos,
EntityOperation<T> operation) {
return !CollectionUtils.isEmpty(dtos)
&& StringUtils.isNotBlank(operation.getActivationFlag(entity))
&& ControlFlag.PARTIAL.getCode().equals(operation.getControlFlag(entity));
}
private <T> void cleanupExistingRelations(T entity, EntityOperation<T> operation) {
Integer identifier = operation.getIdentifier(entity);
if (identifier != null) {
operation.cleanupOldData(dataService, identifier);
}
}
private <T> List<RelationRecord> buildRelationRecords(T entity, List<RelationDto> dtos,
EntityOperation<T> operation) {
return dtos.stream()
.map(dto -> createRelationRecord(dto, entity, operation))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private <T> RelationRecord createRelationRecord(RelationDto dto, T entity,
EntityOperation<T> operation) {
if (dto == null || StringUtils.isBlank(dto.getName())) {
return null;
}
try {
RelationRecord record = new RelationRecord();
BeanUtils.copyProperties(dto, record);
operation.setupRelations(record, entity);
return record;
} catch (Exception e) {
log.warn("创建记录失败: {}", dto.getName(), e);
return null;
}
}
}
设计优势分析
1. 符合开闭原则(OCP)
当需要支持新的实体类型时,只需:
- 创建新的
EntityOperation实现 - 在
EntityOperationHelper中添加策略常量 - 在
DataService中添加相应的数据访问方法
完全无需修改现有代码!
2. 单一职责原则(SRP)
EntityOperation接口:定义操作契约- 具体策略实现:专注特定实体的业务逻辑
EntityManager:统一管理操作流程DataService:专注数据访问
3. 类型安全与编译时检查
使用泛型确保类型安全:
java
public <T> void saveEntityRelations(T entity, List<RelationDto> relationDtos,
EntityOperation<T> operation)
4. 模板方法模式结合
EntityManager 中的方法体现了模板方法模式:
- 验证前置条件
- 清理旧数据
- 构建新数据
- 执行保存操作
- 记录操作结果
实际应用场景
场景1:数据同步
java
@Service
public class DataSyncService {
@Autowired
private EntityManager entityManager;
public void syncBannerData(BannerInfo banner, List<RelationDto> relations) {
entityManager.saveEntityRelations(banner, relations,
EntityOperationHelper.BANNER_OPERATION);
}
public void syncProductData(Product product, List<RelationDto> relations) {
entityManager.saveEntityRelations(product, relations,
EntityOperationHelper.PRODUCT_OPERATION);
}
}
场景2:数据报表生成
java
@Service
public class ReportService {
@Autowired
private EntityManager entityManager;
public Report generateBannerReport(BannerInfo banner) {
List<RelationDto> relations = entityManager.queryEntityRelations(
banner, EntityOperationHelper.BANNER_OPERATION);
return new Report("横幅广告报告", banner.getRefno(), relations);
}
public Report generateProductReport(Product product) {
List<RelationDto> relations = entityManager.queryEntityRelations(
product, EntityOperationHelper.PRODUCT_OPERATION);
return new Report("产品报告", product.getRefno(), relations);
}
}
单元测试
java
@ExtendWith(MockitoExtension.class)
class EntityManagerTest {
@Mock
private DataService dataService;
@InjectMocks
private EntityManager entityManager;
@Test
void testSaveBannerRelations() {
BannerInfo banner = createTestBanner();
List<RelationDto> relations = createTestRelations();
entityManager.saveEntityRelations(banner, relations,
EntityOperationHelper.BANNER_OPERATION);
verify(dataService, times(1)).batchSave(anyList());
}
@Test
void testQueryProductRelations() {
Product product = createTestProduct();
when(dataService.retrieveByProductRefno(anyInt()))
.thenReturn(createTestRecords());
List<RelationDto> result = entityManager.queryEntityRelations(
product, EntityOperationHelper.PRODUCT_OPERATION);
assertFalse(result.isEmpty());
assertEquals(3, result.size());
}
}
总结
通过策略模式的应用,我们成功实现了:
- 高度抽象:将多实体操作的共同点抽象为统一接口
- 灵活扩展:新增实体类型无需修改现有代码
- 易于维护:每个策略类职责单一,逻辑清晰
- 测试友好:每个策略可以独立测试
这种设计模式不仅适用于实体关联管理,还可以广泛应用于:
- 多支付渠道集成
- 多通知方式发送
- 多文件格式处理
- 多数据源访问
策略模式的核心价值在于将变化的部分封装起来,使得系统在面对新的需求变化时能够保持稳定。这种"面向接口编程,而非面向实现编程"的思想,是现代软件设计的重要原则。
在实际项目中,我们应该识别那些看似不同但本质相似的操作,然后运用策略模式将它们统一管理,从而构建出更加健壮、可维护的系统架构。