在编码中经常会遇到过多的 if else ,就会显得代码很臃肿,分支比较多,这种情况肯定是不建议用if-else的,维护起来不是很方便,而且容易出现 bug,下面看下如何解决满屏的 if else;
我项目中的场景: 我们有一个需求是实名认证,但是实名认证有很多种方式,例如: 人脸识别,实名认证四要素(姓名,身份证,手机号,银行卡,主要针对企业),实名认证三要素(姓名,身份证,手机号), 实名认证二要素(姓名,身份证号)等等…这个是由客户自己选择;如果不进行优化代码将是这样的:
//优化前
@Test
public void testNoStrategy(String dsType) {
if (dsType.equals("faceRecognition")) {
FaceRecognition faceRecognition = new FaceRecognition();
faceRecognition.connect(null);
} else if (dsType.equals("threeElements")) {
ThreeElements threeElements = new ThreeElements();
threeElements.connect(null);
} else if (dsType.equals("twoElements")) {
TwoElements twoElements = new TwoElements();
twoElements.connect(null);
} else {
FourElements fourElements = new FourElements();
fourElements.connect(null);
}
}
很臃肿,很难看…
下面就用反射和策略模式
解决一下这种应用场景.
直接看代码:
这是我的项目目录结构;下面我们看看每个类中都写了什么?
DataSourceType
首先定义了一个注解类DataSourceType
:
Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSourceType {
//策略名
String value() default "";
}
DataSourceStrategy
声明一个接口
public interface DataSourceStrategy {
//每个策略的逻辑实现
Map<String, Object> connect(Map<String, String> params);
}
下面我们看看几个策略类:
FaceRecognition
人脸识别功能,在这里写你的业务逻辑,如果的业务逻辑牵扯到事务的话,建议还是在 service 层写;
同时这里实现了上面咱们定义的一个接口DataSourceStrategy
;
注意: @DataSourceType("faceRecognition")
,这里注意一下,后面我们会用到.
@Component
@DataSourceType("faceRecognition")
public class FaceRecognition implements DataSourceStrategy {
@Override
public Map<String, Object> connect(Map<String, String> params) {
//do something....
//返回结果
JSONObject json = new JSONObject();
json.put("type", "人脸识别");
json.put("status", "success");
return json;
}
}
FourElements
实名认证四要素;和上面的人脸识别是一样的道理;三要素和二要素就不一一列举了…
@Component
@DataSourceType("fourElements")
public class FourElements implements DataSourceStrategy {
@Override
public Map<String, Object> connect(Map<String, String> params) {
//do something....
//返回结果
JSONObject json = new JSONObject();
json.put("type", "四要素");
json.put("status", "success");
return json;
}
}
下面我们看下这个工具类做了什么?
CacheCollection
public class CacheCollection {
private static Map<String, Class> ALL_DATA_SOURCE;
static {
ALL_DATA_SOURCE = new HashMap<>(100);
}
/**
* 根据策略名获取 Class
* @param dsType
* @return
*/
public static Class getDS(String dsType) {
return ALL_DATA_SOURCE.get(dsType);
}
/**
* 策略名为 key,Class 为 value
*/
public static void putClass(String dsType,Class clazz){
ALL_DATA_SOURCE.put(dsType,clazz);
}
}
声明一个Map容器,很简单就是策略名@DataSourceType("faceRecognition")
这个注解中 value值faceRecognition
为 key,这个类的Class为value;
InitDataSource
@Component
public class InitDataSource {
@Value("${haoxy.package}")
private String packageVo;
@PostConstruct
public void init() {
initAllDataSourceType(packageVo);
}
private void initAllDataSourceType(String packageVo) {
URL url = this.getClass().getClassLoader().getResource(packageVo.replace(".", "/"));
File dir = new File(url.getFile());
for (File file : dir.listFiles()) {
if (file.isDirectory()) {
continue;
} else {
String className = packageVo + "." + file.getName().replace(".class", "");
try {
Class<?> clazz = Class.forName(className);// class cn.haoxy.ref.inter.impl.faceRecognition
//判断这个类上是否存在指定的注解
if (clazz.isAnnotationPresent(DataSourceType.class)) {
//如果存在,得到此注解的value值
DataSourceType dataSourceType = clazz.getAnnotation(DataSourceType.class);
//放入容器
CacheCollection.putClass(dataSourceType.value(), clazz);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
continue;
}
}
}
}
}
项目在初始化的时候会执行init()
方法,这里用到了反射,利用反射检测策略类上是否含有@DataSourceType
这个注解,如果存在这个注解我们就拿到这个注解上的值,注解上的值为 key,Class为 value 存入Map容器中,其中packageVo
是我在配置文件(application.yml
)中配置的值,也就是策略类所在包的位置;
application.yml
server:
port: 9999
haoxy.package: cn.haoxy.strategy.operation
DataSourceService
@Component
public class DataSourceService {
@Autowired
private DataSourceContextAware dataSourceContextAware;
public Map<String, Object> connect(String dsType, Map<String, String> map) {
DataSourceStrategy dataSourceChild = dataSourceContextAware.getStrategyInstance(dsType);
return dataSourceChild.connect(map);
}
}
这个类也就是下面测试类要调用的;这里先贴出来; 其中dataSourceContextAware.getStrategyInstance(dsType)
这个方法就是我们去Map容器中去取值,dsType
就是下面测试类要传入的值;Map<String, String> map
这个 map是我们需要的其他参数,下面我们贴出这个方法具体的代码;
DataSourceContextAware
@Component
public class DataSourceContextAware implements ApplicationContextAware {
private ApplicationContext applicationContext;
public DataSourceStrategy getStrategyInstance(String dsType) {
Class clazz = CacheCollection.getDS(dsType);
DataSourceStrategy dataSourceStrategy = (DataSourceStrategy) applicationContext.getBean(clazz);
return dataSourceStrategy;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
这里我们获取到DataSourceStrategy
返回回去,同时调用dataSourceChild.connect(map);
至此到这里就结束了,下面我们就贴下测试类;
//优化后
@Test
public void testOperation(){
Map<String, Object> resp = dataSourceService.connect("fourElements", null);
System.out.println(resp);
}
这里我们传入的是fourElements
实名认证四要素;返回结果:
{"type":"四要素","status":"success"}
如果后期我们需要其他的实名认证我们只需要增加策略类即可,这里我一个 if else 都没有用,其实现方式就是策略模式结合反射
;