前言
『代码中if-else是否已经让你不厌其烦? 』
『磨刀霍霍向它的想法是否预谋已久?』
『却苦于没有好的方式?』
来来来,xdm,我就在此献丑,抛转引玉,把自己的解决方式贴出来供大伙儿参考参考。
对于下面这样的代码,大伙肯定都不陌生,当然啦,像这种比较少的if-else,我的理解是一般前期也没有必要去优化,除非说你前期很明确后面有很多类型或者说逻辑需要处理,那在前期能很优雅地解决if-else就很有必要了。
很多时候,项目刚起步,一是业务本身的不明确,二是一般前期项目需求排期都比较近,尽可能高效地完成开发工作比较重要,虽然不至于所有代码都是一把梭,但是后面回头看来,看到自己写的代码,也会忍不住喷一句狗屎
,业务成熟,项目成型后,50%以上的代码都是可优化的(也可能是本人水平过于低下☹)。
无论是前期业务逻辑设计复杂导致的if-else,还是后期屎山代码堆砌的if-else,在我看来,其实就是一种策略思想的应用场景,当然啦,策略模式的话,不知道大伙儿用的多不多,用来解决上述场景再合适不过了。
策略模式
公共接口
首先针对于上图if-else的场景,先定义一个接口,通用的支付接口类
public interface IPay {
void pay();
}
再定义一个注解,用来对每一种的策略进行标记
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PayCode {
String value();
String name();
}
具体实现类
接下来就是具体的实现类
实现类就是具体的策略,当我们需要新增一个策略时,例如美团支付等,直接新增一个实现类即可,不但符合我们常说的开闭原则,还实现了对业务逻辑的解耦,大伙别看下面代码简单,毕竟只是做个实例,在实际场景中,这种逻辑可能让你云里雾里,防不胜防。
@PayCode(value = "ali",name = "支付宝支付")
@Service
public class AliPay implements IPay {
@Override
public void pay() {
System.out.println("--------发起支付宝支付");
}
}
@PayCode(value = "jd",name = "京东支付")
@Service(value = "jdPay")
public class JDPay implements IPay{
@Override
public void pay() {
System.out.println("--------发起了京东支付");
}
}
@PayCode(value = "weiXin",name = "微信支付")
@Service
public class WeiXinPay implements IPay {
@Override
public void pay() {
System.out.println("--------发起了微信支付");
}
}
核心处理类
PayService2,核心逻辑是继承Spring的ApplicationListener,利用监听事件ContextRefreshedEvent,来实现对各个具体实现类的初步加载。
原理是Spring IOC容器加载处理完相应的Bean后,有一个发布事件的动作(ContextRefreshedEvent),其实这是一个Spring IOC 容器给提供的拓展的地方,xdm,讲道理这个东西还是很实用的,无论在日常开发,还是研究框架源码,这种处理机制都很常见,学习一波☺
@Service
public class PayService2 implements ApplicationListener<ContextRefreshedEvent> {
private static Map<String,IPay> payMap = null;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
Map<String,Object> beansWithAnno = applicationContext.getBeansWithAnnotation(PayCode.class);
if (beansWithAnno != null){
payMap = new HashMap<>();
beansWithAnno.forEach((key,value) ->{
String bizType = value.getClass().getAnnotation(PayCode.class).value();
payMap.put(bizType,(IPay) value);
});
}
}
public void pay(String code){
payMap.get(code).pay();
}
}
当然了,要获取Spring 容器里Bean,对Bean做一些需要的操作,起到随用随取的效果,当然不止这一种用法了。
我很贴心滴,再为大家贴一种写法。
PayService3,和PayService2可以得到一样的效果
继承ApplicationContextAware ,
ApplicationContextAware接口只有一个方法,如果实现了这个方法,那么Spring创建这个实现类的时候就会自动执行这个方法,把ApplicationContext注入到这个类中,也就是说,spring 在启动的时候就需要实例化这个 class(如果是懒加载就是你需要用到的时候实例化),在实例化这个 class 的时候,发现它包含这个 ApplicationContextAware 接口的话,sping 就会调用这个对象的 setApplicationContext 方法,把 applicationContext Set 进去了。
知识点: 一般我们经常会使用这个类来获取Spring的IOC容器
@Service
public class PayService3 implements ApplicationContextAware {
private ApplicationContext applicationContext;
private static final String SUFFIX = "Pay";
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void toPay(String payCode){
((IPay)applicationContext.getBean(getBeanName(payCode))).pay();
}
public String getBeanName(String payCode){
return payCode + SUFFIX;
}
}
具体使用类
/**
* @description:
* @author: zhanghailang
* @date: 2022-1-24 14:43
*/
@RestController
public class TestContrller {
@Autowired
private PayService2 payService2;
@Autowired
private PayService3 payService3;
@PutMapping(value = "/test/{payCode}")
public void test(@PathVariable("payCode") String payCode){
// if ("ali".equals(payCode)) {
// System.out.println("ali pay start");
// }
// if ("jd".equals(payCode)) {
// System.out.println("jd pay start");
// }
// if ("weixin".equals(payCode)) {
// System.out.println("weixin pay start");
// }
//.....
this.payService2.pay(payCode);
this.payService3.toPay(payCode);
}
}
请求结果如下:
通过上面的演示,大家应该能够看到这种策略模式实现减少if-else的优缺点
『优点:代码整洁、解耦,符合开闭原则,职责分明,代码优雅
缺点:每个实现都需要新增个类,当策略过多的时候,就要考虑使用混合模式来优化策略模式了。』
函数式编程
『☺嘿,是不是没想到,这怎么还能用函数式编程吗,且听我为大家稍微的讲解下』
FunctionPayService 和 PayService4,利用@PostConstruct,Supplier<>接口,根据不同的策略,将定义的具体业务操作类FunctionPayService,封装在map里,在使用的时候随用随取即可。
/**
* @description: 具体的业务分类
* @author: zhanghailang
* @date: 2022/1/25 17:18
*/
@Service
public class FunctionPayService {
public String aliPay() {
System.out.println("ali Pay start");
return "ali";
}
public String weiXinPay() {
System.out.println("weiXin Pay start");
return "weinXin";
}
public String jdPay() {
System.out.println("jd Pay start");
return "jd";
}
}
/**
* @description: 使用Lamda 函数式编程减少 if else
* 参考公众号:https://mp.weixin.qq.com/s/79go9AFZgcnNM7uk_5Yaew
* @author: zhanghailang
* @date: 2022/1/25 17:09
*/
@Service
public class PayService4 {
@Autowired
private FunctionPayService functionPayService;
private Map<String, Supplier<String>> payMap = new HashMap<>();
/**
* 初始化业务分派逻辑,代替了if-else 部分
*/
@PostConstruct
public void dispatcherInit() {
payMap.put("ali",() -> functionPayService.aliPay());
payMap.put("weiXin",() -> functionPayService.weiXinPay());
payMap.put("jd",() -> functionPayService.jdPay());
}
public void pay(String payCode){
this.payMap.get(payCode).get();
}
}
上述函数式编程的减少if-else的结果和策略模式一样。
函数式编程用起来是真滴好用,有不懂的xdm,建议学习一波,但是不建议滥用,曾经看过一段代码,函数式编程,纯Lamda表达式,套着各种业务逻辑,给我看晕了,强烈不建议在函数式编程里,写大量业务逻辑。
以上就是小弟的拙见,大家新年快乐啦