如何优雅的替换掉Java代码中的if else
场景
平时我们在写代码时,需要针对不同情况处理不同的业务逻辑,用得最多的就是if和else。但是如果情况太多,就会出现一大堆的“ifelse”,这就是为什么很多遗留系统中,一个函数可能出现上千行的代码。当然你说可以通过抽取方法或者类来实现,每一个情况交给一个方法或者对应一个类来处理,但是这样做只是看起来代码整洁了一些,还是有大量的”ifelse",后面有新的逻辑时,又要添加更多的“ifelse",没有从根本上解决问题。
举个例子,短信发送业务的实现,一般公司会接入多个短信供应商,比如梦网、玄武、阿里云等多个短信平台(我们称之为短信渠道),可能需要针对不同的短信类型或者短信平台的稳定性来切换短信渠道:
比如阿里云短信管控很严,带营销字样的短信不让发送,则营销类短信需要使用其他短信渠道来发送;
也有可能某个短信平台服务挂了暂时不可用,需要切换到另一个短信渠道;
某些短信平台有优惠,则需要临时切换到该短信渠道发送短信;
…
代码实现
上面的业务场景简单来说就是:针对不同的短信渠道来调用对应的短信平台接口实现短信发送。
短信渠道一般配置在文件中,或者配置在数据库中。
代码实现如下(注意下面所有的代码都不能直接运行,只是关键逻辑部分的示例代码):
烂代码示例
我们有一个短信发送类:SmsSendService,里面有一个send方法发送短信
SmsSendService.java
publicclassSmsSendService{ /** *@ParamphoneNo手机号 *@Paramcontent短信内容 */ publicvoidsend(StringphoneNo,Stringcontent){ //从配置中读取短信渠道 StringchannelType=config.getChannelType(); //如果是短信渠道A,则调用渠道A的api发送 if(Objects.equals(channelType,"CHANNEL_A")){ System.out.println("通过短信渠道A发送短信"); } //如果是短信渠道B,则调用渠道B的api发送 elseif(Objects.equals(channelType,"CHANNEL_B")){ System.out.println("通过短信渠道B发送短信"); } } }
如果某天增加了一个短信渠道C,那么接着追加一个”elseif…"
//...此处省略部分代码... //从配置中读取短信渠道 StringchannelType=config.getChannelType(); //如果是短信渠道A,则调用渠道A的api发送 if(Objects.equals(channelType,"CHANNEL_A")){ System.out.println("通过短信渠道A发送短信"); } //如果是短信渠道B,则调用渠道B的api发送 elseif(Objects.equals(channelType,"CHANNEL_B")){ System.out.println("通过短信渠道B发送短信"); } //ADD:如果是短信渠道C,则调用渠道C的api发送 elseif(Objects.equals(channelType,"CHANNEL_C")){ System.out.println("通过短信渠道C发送短信"); } //...此处省略部分代码...
如果又加其他短信渠道了呢?你又写一个“elseif…"?
显然这种做法不可取,也不符合SOLID原则中的”开闭原则“——对扩展开放,对更改封闭。
这样我们每次都需要修改原有代码(对更改没有封闭),不断的添加”ifelse"。
接下来我们把代码优化一下:
优化代码1
定义一个短信渠道的接口SmsChannelService,所有的短信渠道API都实现该接口;
短信渠道接口SmsChannelService.java
publicinterfaceSmsChannelService{ //发送短信 voidsend(StringphoneNo,Stringcontent); }
短信渠道ASmsChannelServiceImplA.java
publicclassSmsChannelServiceImplAimplementsSmsChannelService{ publicvoidsend(StringphoneNo,Stringcontent){ System.out.println("通过短信渠道A发送短信"); } }
短信渠道BSmsChannelServiceImplB.java
publicclassSmsChannelServiceImplBimplementsSmsChannelService{ publicvoidsend(StringphoneNo,Stringcontent){ System.out.println("通过短信渠道B发送短信"); } }
通过工厂类来初始化所有短信渠道service
SmsChannelFactory.java
publicclassSmsChannelFactory{ privateMapserviceMap; //初始化工厂,将所有的短信渠道Service放入Map中 publicSmsChannelFactory(){ //渠道类型为key,对应的服务类为value: serviceMap=newHashMap (2); serviceMap.put("CHANNEL_A",newSmsChannelServiceImplA()); serviceMap.put("CHANNEL_B",newSmsChannelServiceImplB()); } //根据短信渠道类型获得对应渠道的Service publicSmsChannelServicebuildService(StringchannelType){ returnserviceMap.get(channelType); } }
在原来的SmsSendService中调用不同短信渠道的接口。
原来的SmsSendService类优化如下
publicclassSmsSendService{ privateSmsChannelFactorysmsChannelFactory; publicSmsSendService(){ smsChannelFactory=newSmsChannelFactory(); } publicvoidsend(StringphoneNo,Stringcontent){ //从配置中读取短信渠道 StringchannelType=config.getChannelType(); //获取渠道类型对应的服务类 SmsChannelServicechannelService=smsChannelFactory.buildService(channelType); //发送短信 channelService.send(phoneNo,content); } }
这样SmsSendService类非常简洁,把“ifelse"干掉了,
如果我要增加一个短信渠道C,无需再次更改SmsSendService类。
只需要增加一个类SmsChannelServiceImplC实现SmsChannelService接口,
然后在工厂类SmsChannelFactory中增加一行初始化SmsChannelServiceImplC的代码即可。
增加短信渠道C的实现SmsChannelServiceImplC.java
publicclassSmsChannelServiceImplCimplementsSmsChannelService{ publicvoidsend(StringphoneNo,Stringcontent){ System.out.println("通过短信渠道C发送短信"); } }
修改工厂类SmsChannelFactory.java
publicclassSmsChannelFactory{ privateMapserviceMap; //初始化serviceMap,将所有的短信渠道Service放入Map中 publicSmsChannelFactory(){ //渠道类型为key,对应的服务类为value: serviceMap=newHashMap (3); serviceMap.put("CHANNEL_A",newSmsChannelServiceImplA()); serviceMap.put("CHANNEL_B",newSmsChannelServiceImplB()); //ADD增加一行SmsChannelServiceImplC的初始化代码 serviceMap.put("CHANNEL_C",newSmsChannelServiceImplC()); } //根据渠道类型构建短信渠道Service publicSmsChannelServicebuildService(StringchannelType){ returnserviceMap.get(channelType); } }
“ifelse"是干掉了,但还是得修改原来的类SmsChannelFactory,不满足"开闭原则",有没有更好得方式呢?
我们通过使用spring的依赖注入进一步优化代码:
优化代码2
SmsChannelService接口增加getChannelType()方法,这一步很关键。
publicinterfaceSmsChannelService{ //发送短信 voidsend(StringphoneNo,Stringcontent); //关键:增加getChannelType()方法,子类实现这个方法用于标识出渠道类型 StringgetChannelType(); }
子类增加该方法的实现,并加上@Service注解,使其让spring容器管理起来
SmsChannelServiceImplA.java
@Service publicclassSmsChannelServiceImplAimplementsSmsChannelService{ publicvoidsend(StringphoneNo,Stringcontent){ System.out.println("通过短信渠道A发送短信"); } //关键:增加getChannelType()实现 publicStringgetChannelType(){ return"CHANNEL_A"; } }
SmsChannelServiceImplB.java
@Service publicclassSmsChannelServiceImplBimplementsSmsChannelService{ publicvoidsend(StringphoneNo,Stringcontent){ System.out.println("通过短信渠道B发送短信"); } //关键:增加getChannelType()实现 publicStringgetChannelType(){ return"CHANNEL_B"; } }
修改SmsChannelFactory类:这一步也很关键。
SmsChannelFactory.java
@Service publicclassSmsChannelFactory{ privateMapserviceMap; /*注入:通过spring容器将所有实现SmsChannelService接口的类的实例注入到serviceList中*/ @Autowired privateList serviceList; /*通过@PostConstruct注解,在SmsChannelFactory实例化后,来初始化serviceMap*/ @PostConstruct privatevoidinit(){ if(CollectionUtils.isEmpty(serviceList)){ return; } serviceMap=newHashMap (serviceList.size()); //将serviceList转换为serviceMap for(SmsChannelServicechannelService:serviceList){ StringchannelType=channelService.getChannelType(); //重复性校验,避免不同实现类的getChannelType()方法返回同一个值。 if(serviceMap.get(channelType)!=null){ thrownewRuntimeException("同一个短信渠道只能有一个实现类"); } /*渠道类型为key,对应的服务类为value: 与“优化代码1”中的通过手工设置“CHANNEL_A"、"CHANNEL_B"相比, 这种方式更加自动化,后续在增加“CHANNEL_C"无需再改此处代码*/ serviceMap.put(channelType,channelService); } } //根据渠道类型获取对应短信渠道的Service publicSmsChannelServicebuildService(StringchannelType){ returnserviceMap.get(channelType); } }
SmsSendService加上@Service注解。通过@Autowired注入SmsChannelFactory
SmsSendService.java
@Service publicclassSmsSendService{ @Autowired privateSmsChannelFactorysmsChannelFactory; publicvoidsend(StringphoneNo,Stringcontent){ //从配置中读取短信渠道类型 StringchannelType=config.getChannelType(); //构建渠道类型对应的服务类 SmsChannelServicechannelService=smsChannelFactory.buildService(channelType); //发送短信 channelService.send(phoneNo,content); } }
这时,如果需要添加一个渠道C,那真的只需要添加一个SmsChannelServiceImplC即可,再也不用改原有代码,完全遵循“开闭原则”。
SmsChannelServiceImplC.java
@Service publicclassSmsChannelServiceImplCimplementsSmsChannelService{ publicvoidsend(StringphoneNo,Stringcontent){ System.out.println("通过短信渠道C发送短信"); } publicStringgetChannelType(){ return"CHANNEL_C"; } }
以上就是如何优雅的替换掉Java代码中的ifelse的详细内容,更多关于替换代码中的ifelse的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。