Java使用策略模式实现多实体通用操作的优雅设计

引言:多实体操作抽象化的挑战

在现代软件系统中,我们经常需要处理多种不同类型的实体对象,这些实体虽然具有不同的业务含义和数据结构,但在操作层面却有着许多相似之处。例如,无论处理横幅广告、产品还是商城产品,我们都需要进行增删改查、数据转换、权限验证等操作。

本文将通过一个重构后的代码案例,展示如何运用策略模式优雅地解决多实体操作中的代码重复和复杂性管理问题。

重构前的问题

假设我们有一个系统,需要处理以下三种不同类型的实体:

  1. BannerInfo(横幅广告信息)
  2. Product(产品信息)
  3. MallProduct(商城产品信息)

每种实体都有:

  • 唯一的标识符(refno)
  • 特定的控制标志位
  • 不同的数据存储方式

在传统实现中,我们可能会为每种实体编写重复的代码,或者使用大量的 if-elseswitch 语句来判断实体类型,这会导致:

  • 代码重复,维护困难
  • 新增实体类型时需要修改大量代码
  • 难以进行单元测试

策略模式的完美应用

核心思想

我们通过定义一个通用操作接口,为每种实体提供具体的实现策略,从而实现"同一个接口,不同实现"的目标。

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)

当需要支持新的实体类型时,只需:

  1. 创建新的 EntityOperation 实现
  2. EntityOperationHelper 中添加策略常量
  3. DataService 中添加相应的数据访问方法

完全无需修改现有代码!

2. 单一职责原则(SRP)

  • EntityOperation 接口:定义操作契约
  • 具体策略实现:专注特定实体的业务逻辑
  • EntityManager:统一管理操作流程
  • DataService:专注数据访问

3. 类型安全与编译时检查

使用泛型确保类型安全:

java 复制代码
public <T> void saveEntityRelations(T entity, List<RelationDto> relationDtos, 
                                    EntityOperation<T> operation)

4. 模板方法模式结合

EntityManager 中的方法体现了模板方法模式:

  1. 验证前置条件
  2. 清理旧数据
  3. 构建新数据
  4. 执行保存操作
  5. 记录操作结果

实际应用场景

场景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());
    }
}

总结

通过策略模式的应用,我们成功实现了:

  1. 高度抽象:将多实体操作的共同点抽象为统一接口
  2. 灵活扩展:新增实体类型无需修改现有代码
  3. 易于维护:每个策略类职责单一,逻辑清晰
  4. 测试友好:每个策略可以独立测试

这种设计模式不仅适用于实体关联管理,还可以广泛应用于:

  • 多支付渠道集成
  • 多通知方式发送
  • 多文件格式处理
  • 多数据源访问

策略模式的核心价值在于将变化的部分封装起来,使得系统在面对新的需求变化时能够保持稳定。这种"面向接口编程,而非面向实现编程"的思想,是现代软件设计的重要原则。

在实际项目中,我们应该识别那些看似不同但本质相似的操作,然后运用策略模式将它们统一管理,从而构建出更加健壮、可维护的系统架构。

相关推荐
茶本无香2 小时前
设计模式之三—工厂模式:灵活对象创建的艺术
java·开发语言·设计模式·工厂模式
超级无敌大学霸2 小时前
c语言整型提升
c语言·开发语言
困惑阿三2 小时前
利用 Flexbox 实现无需媒体查询(Media Queries)的自动响应式网格。
开发语言·前端·javascript
m0_748250032 小时前
C++ 预处理器
开发语言·c++
week_泽2 小时前
第7课:管理长期记忆的关键架构决策 - 学习笔记_7
java·笔记·学习·ai agent
袁袁袁袁满2 小时前
Python使用uuid生成唯一密钥uid详细教程
开发语言·python·uuid·唯一密钥uid
Logan Lie2 小时前
Go 反射(Reflection)详解:从入门到实践
开发语言·后端·golang
爱装代码的小瓶子2 小时前
【c++进阶】c++11下类的新变化以及Lambda函数和封装器
java·开发语言·c++
乌萨奇也要立志学C++2 小时前
【Linux】线程同步 条件变量精讲 + 生产者消费者模型完整实现
java·linux·运维