前言
在项目中有一个场景:根据不同的地区对数据进行不同方式的转换和整合。避免后续产生大量的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");
}
}