如何更优雅的结合Spring使用策略模式解决if-else问题
一.背景
在写业务代码的时候,难免会遇到很多if-else,这个时候如果if-else不是很多可以用if-else。如果此时场景过多,太多的if-else会导致代码比较臃肿,所以这个时候就需要抽象化,将每个场景独立开来,定义一个顶层接口,不同场景有不同实现,这个时候策略模式就出现了,之前写过的文章有写过策略模式https://blog.csdn.net/qq_45136592/article/details/122979761?spm=1001.2014.3001.5501,但是有一段代码让我比较疑惑
@Component
public class AddWhiteIpProxyFactory implements InitializingBean, ApplicationContextAware {
//存储代理类型处理器的容器
private static final Map<AddPublicIpProxyType, AddWhiteIpConfigHandler> ADD_WHITE_PUBLIC_IP_MAP = new EnumMap<>(AddPublicIpProxyType.class);
private ApplicationContext appContext;
/**
* 根据代理类型获取对应添加方式
*
* @param addPublicIpProxyType 添加白名单类型
* @return AddWhiteIpConfigService 返回添加白名单的类型处理器
*/
public AddWhiteIpConfigHandler getHandler(AddPublicIpProxyType addPublicIpProxyType) {
return ADD_WHITE_PUBLIC_IP_MAP.get(addPublicIpProxyType);
}
@Override
public void afterPropertiesSet() {
try {
//从spring容器中获取对应的bean,并判断是否为AddWhiteIpConfigHandler的实现,添加到ADD_WHITE_PUBLIC_IP_MAP中
appContext.getBeansOfType(AddWhiteIpConfigHandler.class)
.values()
.forEach(value->ADD_WHITE_PUBLIC_IP_MAP.put(value.getAddPublicIpProxyType(),value));
} catch (BeansException e) {
LoggerHelper.error("初始化添加对象失败");
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
try {
//获取spring容器
appContext=applicationContext;
} catch (Exception exception) {
LoggerHelper.error("初始化获取spring容器失败");
}
}
}
上面这段代码的主要作用,就是将spring容器中的对象存到我们自定义的Map容器中,Key的名称是我们自定义的枚举,这样做有一个坏处,如果后续有其他场景也符合这种场景,那也需要向Map中加入对应场景的Spring容器中的对象,比较繁琐,所以一直在想有没有什么合适的方式能解决这个方式问题。
二.探索
既然知道自己想要什么,那就开始google了,关键字’如何更加优雅的使用策略模式解决if-else问题’,果不其然在看资料的时候找到了答案,看到了解决方案恍然大悟,我们都知道我们把对象交给spring管理其实都是存在一个map的缓存中,spring中有一个方法叫做getBeansOfType这个方法是判断对象归属于哪种类型的其实就是判断他的父类是哪个,既然是这样就能从Map缓存中找到我们所定义的接口实现然后再将他放入另外一个缓存中,这个时候就引入了@Autowired注入类的集合类型和Map类型,平时工作的时候都是注入单个对象,很少用到注入List和Map类型所以很难往那方面想到,顿时恍然大悟。
三.实践
1.定义接口
package com.ljm.service;
/**
* @Author jmle
* @Date 2022/9/6 14:22
* @Version 1.0
*/
public interface ILoginStrategy<T> {
/**
* 登录处理逻辑
*
* @param value
* @return Result<XxxxDO>
*/
void doLogin(T value);
}
2.定义实现
package com.ljm.service;
import org.springframework.stereotype.Service;
/**
* @Author jmle
* @Date 2022/9/6 14:23
* @Version 1.0
*/
@Service
public class ALoginStrategy implements ILoginStrategy<String> {
@Override
public void doLogin(String value) {
System.out.println("A登录方式登录成功");
}
}
package com.ljm.service;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
/**
* @Author jmle
* @Date 2022/9/6 14:24
* @Version 1.0
*/
@Service
public class BLoginStrategy implements ILoginStrategy<String> {
@Override
public void doLogin(String value) {
System.out.println("B登录方式成功");
}
}
3.测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyLoveApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ApplicationTest {
@Autowired
private List<ILoginStrategy<String>> iLoginStrategyList;
@Autowired
private Map<String, ILoginStrategy<String>> iLoginStrategyMap;
@Test
public void testAutowiredList(){
iLoginStrategyList.forEach(c-> c.doLogin("哈哈哈"));
}
@Test
public void testAutowiredMap(){
iLoginStrategyMap.keySet().forEach(System.out::println);
}
}
4.结果
测试注入iLoginStrategyList打印结果:
A登录方式登录成功
B登录方式成功
测试注入iLoginStrategyMap打印Key的值:
ALoginStrategy
BLoginStrategy
5.结论
通过上面的结果我们可以看出,不管是注入list还是注入Map,都能够把ILoginStrategy的实现注入到我们想要的地方,针对注入Map我们可以看出打印的Key对应的值是相应实现的类名,如果想要用此种方式解决过多if-else的问题,我们可以把相应的场景名称定义成枚举,然后通过相应枚举对应的值从我们注入的Map获取相应的实现然后达到调用相应的逻辑.
四.总结
盲猜spring肯定是判断注入的对象是何种类型,然后从Map中找出对应的实现,然后返回,具体源码还未来得及阅读,本次不做阐述