前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊一聊去if-else方案

聊一聊去if-else方案

作者头像
叔牙
发布2020-11-19 15:01:15
4980
发布2020-11-19 15:01:15
举报
文章被收录于专栏:一个执拗的后端搬砖工

稍微有编程常识的人都知道,if-else分支条件是任何编程语言任何业务中必不可少的环节,但是过多的if-else分支让代码变得臃肿不可维护,虽然说switch case某种程度上可以替代if-else让代码变得调理有序,但是没有从根本上解决问题。那么有没有比较好的方式来解决这个问题呢?当然是有,我们不妨先看一个案例:

案例一 自定义退货地址问题

1. 背景描述

会员发起退货退款后,自动化退款逻辑会根据退款信息,所属店铺信息调用自动寻址逻辑,然后把退货地址返回给会员,但是店铺A突然间提了个需求,对于我店铺A的所有退货退款,退货地址我们自己维护一套,你们退款业务从

退款单中识别到是我A店铺的单子后调用我们自己维护的寻址逻辑,然后返回给会员。

2. 解决方案

对于1中的需求,我们不考虑扩展性的情况下很好解决,我在同一退货逻辑中加一个if条件判断,通过店铺A给我的订单标或者店铺id,如果是店铺A的单子我走他们的寻址逻辑,否则走默认的寻址逻辑。用伪代码描述就是:

if(退款单属于店铺A) {

return 店铺A退货地址

} else {

return 通用退货地址

}

3.扩展性

有一天店铺B发现他们的退货地址库没有入菜鸟仓,也需要我们对其退货地址从通用逻辑剥离单独维护,那么我们

的代码逻辑将变成这样:

if(退款单属于店铺A) {

return 店铺A退货地址

} else if(退款单属于店铺B){

return 店铺B退货地址

}

else {

return 通用退货地址

}

那如果后边有店铺C,店铺D,针织100多家店铺都需要实现这种特殊逻辑,我们将如何维护,如果不想出更好的方案,那么代码将变成真正的“面条代码”:

if(退款单属于店铺A) {

return 店铺A退货地址

} else if(退款单属于店铺B){

return 店铺B退货地址

}

···

···

else {

return 通用退货地址

}

案例二 退款类型问题

1.背景描述

在自动化退款流程中,会员发起退款后,逆向交易链路会给我们发送退款消息,自动化退款逻辑会从退款单中识别或者计算出退款类型,并执行相应的逻辑。如果之前只有仅退款类型,那么如果新业务要支持同意退货怎么办?

2.解决方案

对于退款类型新增同意退货,那么可以直接在逻辑中增加if判断支持该类型,并调用相应的逻辑。伪代码:

if(退款类型 是 同意退货) {

同意退货

} else {

仅退款

}

3.扩展性

有一天新需求来了,自动化退款要支持同意退货,那么我们仍可以考虑直接加if-else逻辑,代码逻辑将变成这样:

if(退款类型 是 同意退货) {

同意退货

} else if(退款类型 是 确认收货) {

确认收货

}

else {

仅退款

}

那么后续如果随着业务的发展,需要支持到赔付、换货以及维修等,那么我们将何去何从,继续修改上边的“面条代码”?

if(退款类型 是 同意退货) {

同意退货

} else if(退款类型 是 确认收货) {

确认收货

}

···

···

else {

仅退款

}

显然,这种代码是不可维护的,并且如果交给新人接手,估计内心是崩溃的,改没法改,重构鬼才知道中间有没有什么大坑,整个项目就被这种代码搞得满目疮痍。

解决方案

对于上述两个问题,在讲述解决方案之前先看下图:

从两张图中我们大概可以看出几个关键的点:

1)请求上下文信息中一定要有一个能够抽象出来有辨识度的属性(比如退款类型,或者店铺id)

2)一定要有一个地方存储1)中有辨识度的属性与我们自己定义的执行器的标签的映射关系(配置中心或者DB)

3)要有一个容器存储所有自定义执行器(和spring容器中其他bean隔离),并且能够根据标签获取到响应的执行器

基于以上描述,我们已经有了方案,接下来分别针对退货地址案例给出具体的实现方案。

退货地址接口:

/**

* 寻址接口

*/

public interface ReturnAddressService {

String getAddress(Long sellerId);

String getCode();

}

退货地址接口抽象实现:

/**

* 自定义寻址抽象实现

*/

public abstract class AbstractReturnAddressService implements ReturnAddressService {

@Override

public String getAddress(Long sellerId) {

if(null == sellerId) {

throw new IllegalArgumentException("params is illegal");

}

return execute(sellerId);

}

public abstract String execute(Long sellerId);

}

店铺A自定义寻址实现:

/**

* 店铺A自定义寻址

*/

@Service("storeAReturnAddressService")

public class StoreAReturnAddressServiceImpl extends AbstractReturnAddressService {

@Override

public String execute(Long sellerId) {

return "店铺A退货地址";

}

@Override

public String getCode() {

return StoreReturnAddressEnum.STORE_A.getCode();

}

}

店铺B自定义寻址实现:

/**

* 店铺B自定义寻址

*/

@Service("storeBReturnAddressService")

public class StoreBReturnAddressServiceImpl extends AbstractReturnAddressService {

@Override

public String execute(Long sellerId) {

return "店铺B退货地址";

}

@Override

public String getCode() {

return StoreReturnAddressEnum.STORE_B.getCode();

}

}

自定义寻址执行器容器:

/**

* 自定义寻址容器

*/

@Component

public class CustomReturnAddressHolder implements ApplicationContextAware {

private static Map<String,ReturnAddressService> map = new HashMap<>();

/**

* 初始化容器

*

* @param applicationContext

* @throws BeansException

*/

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

if(null != applicationContext) {

Map<String,ReturnAddressService> beans = applicationContext.getBeansOfType(ReturnAddressService.class);

if(null != beans && !beans.isEmpty()) {

beans.forEach((k,v) -> {

map.put(v.getCode(),v);

});

}

}

}

/**

* 获取自定义执行器

*

* @param code

* @return

*/

public ReturnAddressService getByCode(String code) {

return map.get(code);

}

}

店铺与执行器code映射关系(暂用本地map代替):

static Map<String,String> map = new HashMap<>();

static {

//店铺与执行器映射关系

map.put(StoreReturnAddressEnum.STORE_A.getCode(),"store_a");

map.put(StoreReturnAddressEnum.STORE_B.getCode(),"store_b");

}

测试验证代码:

public static void main(String[] args) {

AbstractApplicationContext context = new ClassPathXmlApplicationContext("spring-root.xml");

CustomReturnAddressHolder returnAddressHolder = context.getBean("customReturnAddressHolder",CustomReturnAddressHolder.class);

String storeA = "store_a";

String storeB = "store_b";

String executorCodeA = null;

String executorCodeB = null;

for(Map.Entry<String,String> entry : map.entrySet()) {

if(entry.getValue().equals(storeA)) {

executorCodeA = entry.getKey();

continue;

}

if(entry.getValue().equals(storeB)) {

executorCodeB = entry.getKey();

}

}

ReturnAddressService storeAService = returnAddressHolder.getByCode(executorCodeA);

ReturnAddressService storeBService = returnAddressHolder.getByCode(executorCodeB);

System.out.println(storeAService.getAddress(1L));

System.out.println(storeBService.getAddress(2L));

}

运行测试代码:

根据运行结果,我们看到已经通过利用spring自定义容器加上执行器自带code取巧方案,我们已经实现了消除if-else代码的初衷。并且如果扩展店铺C只需要增加自定义执行器即可,完全不需要修改业务逻辑和容器逻辑,也就实现了执行器和业务逻辑剥离,更高一层也即是业务域和底层平台调用链剥离,从而使我们的业务和代码都变得清晰有致。当然这只是简单的实现了if-else逻辑的消除,阿里内部平台基本都是用了TMF的架构模式,其本质上的原理也是自定义执行器,自定义扩展点,动态加载。至于TMF框架,阿里暂没有开源,想了解的话暂时优点困难。

总结

通过上述一系列描述,我们简单实现了使用业务执行器自定义扩展点的方式去除繁杂的if-else逻辑,让程序变得清晰有条理并且便于扩展,希望在日常开发和学一种对大家有帮助,如果感兴趣可以私聊我,共同探讨共同进步。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-07-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PersistentCoder 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档