【在SpringBoot中使用策略模式】


前言

在项目中有一个场景:根据不同的地区对数据进行不同方式的转换和整合。避免后续产生大量的if-else和switch-case,考虑使用策略模式进行改造。

考虑到这块转换逻辑比较复杂,遂采用基于接口的策略形式,每个实现类是一种转换方式。


基本使用

1. 创建策略能力接口

public interface DataTransformStrategy {
    String getArea();

    void transform();
}

2. 创建接口实例

@Component
@Slf4j
public class TransformMethodA implements DataTransformStrategy {
    @Override
    public String getArea() {
        return "A";
    }

    @Override
    public void transform() {
        log.info("转换方式A");
    }
}
@Component
@Slf4j
public class TransformMethodB implements DataTransformStrategy {
    @Override
    public String getArea() {
        return "B";
    }

    @Override
    public void transform() {
        log.info("转换方式B");
    }
}

3. 创建策略上下文,用于确定策略并执行对应策略的实际执行方法

@Component
@Slf4j
public class TransformService implements ApplicationContextAware {

    private Map<String, DataTransformStrategy> strategies = new ConcurrentHashMap<>();

    public void transform(String method) {
        // 使用时根据提供的策略标志(地区)从map中获取实现类并调用其执行方法
        final DataTransformStrategy strategy = Optional.ofNullable(strategies.get(method))
                .orElseThrow(() -> {
                    log.error("未找到该转换方式");
                    return new IllegalArgumentException("未找到该转换方式");
                });
        strategy.transform();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // 获取所有实现DataTransformStrategy接口的Spring Bean
        final Map<String, DataTransformStrategy> beans = applicationContext.getBeansOfType(DataTransformStrategy.class);
        // 然后将地区当作key,策略实现类作为value放入map中
        beans.values().forEach(strategy -> strategies.put(strategy.getArea(), strategy));
    }
}

4. 使用策略上下文

@SpringBootTest(classes = StrategyApplication.class)
@DisplayName("策略模式测试")
class TransformServiceTest {
    @Autowired
    private TransformService transformService;

    @Test
    public void testTransformService() {
        transformService.transform("A");
        transformService.transform("B");
    }
}

扩展

其实对于一个接口有多个实现类的这种场景,IoC容器会帮我们处理成以bean name为key,bean为value的Map。基于此,上述第3步中的TransformService可以不需要实现ApplicationContextAware接口,直接声明一个对应类型的Map,使用@Autowired注入即可,第三步代码可以改为:

@Component
@Slf4j
public class TransformService {

    /**
     * ioc容器会自动注入以bean name为key,DataTransformStrategy的实现类为value的map
     */
    @Autowired
    private Map<String, DataTransformStrategy> strategies = new ConcurrentHashMap<>();

    public void transform(String method) {
        // 使用时根据提供的策略标志(地区)从map中获取实现类并调用其执行方法
        final DataTransformStrategy strategy = Optional.ofNullable(strategies.get(method))
                .orElseThrow(() -> {
                    log.error("未找到该转换方式");
                    return new IllegalArgumentException("未找到该转换方式");
                });
        strategy.transform();
    }
}

由于是以bean name为key,那我们在策略接口实现类上指定名称即可。然后你会发现我们用于区分策略的标志方法getArea也没有用处了,最后策略接口以及实现类(以转换方式A为例)变成了这样子:

// 策略接口
public interface DataTransformStrategy {
    void transform();
}
// 指定bean name,ioc会自动将其作为key放到map中
@Component("A")
@Slf4j
public class TransformMethodA implements DataTransformStrategy {
  
    @Override
    public void transform() {
        log.info("转换方式A");
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值