如何优雅的替换掉Java代码中的if else

场景

平时我们在写代码时,需要针对不同情况处理不同的业务逻辑,用得最多的就是if和else。 但是如果情况太多,就会出现一大堆的“if else”,这就是为什么很多遗留系统中,一个函数可能出现上千行的代码。当然你说可以通过抽取方法或者类来实现,每一个情况交给一个方法或者对应一个类来处理,但是这样做只是看起来代码整洁了一些,还是有大量的”if else",后面有新的逻辑时,又要添加更多的“if else",没有从根本上解决问题。

举个例子,短信发送业务的实现,一般公司会接入多个短信供应商,比如梦网、玄武、阿里云等多个短信平台(我们称之为短信渠道),可能需要针对不同的短信类型或者短信平台的稳定性来切换短信渠道:

比如阿里云短信管控很严,带营销字样的短信不让发送,则营销类短信需要使用其他短信渠道来发送;
也有可能某个短信平台服务挂了暂时不可用,需要切换到另一个短信渠道;
某些短信平台有优惠,则需要临时切换到该短信渠道发送短信;

代码实现

上面的业务场景简单来说就是:针对不同的短信渠道来调用对应的短信平台接口实现短信发送。
短信渠道一般配置在文件中,或者配置在数据库中。

代码实现如下(注意下面所有的代码都不能直接运行,只是关键逻辑部分的示例代码):

烂代码示例

我们有一个短信发送类:SmsSendService,里面有一个send方法发送短信

SmsSendService.java

public class SmsSendService{
	/**
 	 * @Param phoneNo 手机号
	 * @Param content 短信内容
	 */
	public void send(String phoneNo,String content){
		//从配置中读取 短信渠道
		String channelType=config.getChannelType();

		//如果是短信渠道A,则调用渠道A的api发送
		if(Objects.equals(channelType,"CHANNEL_A")){
			System.out.println("通过短信渠道A发送短信");
		}
		//如果是短信渠道B,则调用渠道B的api发送
		else if(Objects.equals(channelType,"CHANNEL_B")){
			System.out.println("通过短信渠道B发送短信");
		}
	}
}

如果某天增加了一个短信渠道C,那么接着追加一个”else if…"

//... 此处省略部分代码 ...

//从配置中读取 短信渠道
String channelType=config.getChannelType();
//如果是短信渠道A,则调用渠道A的api发送
if(Objects.equals(channelType,"CHANNEL_A")){
	System.out.println("通过短信渠道A发送短信");
}
//如果是短信渠道B,则调用渠道B的api发送
else if(Objects.equals(channelType,"CHANNEL_B")){
	System.out.println("通过短信渠道B发送短信");
}
//ADD: 如果是短信渠道C,则调用渠道C的api发送
else if(Objects.equals(channelType,"CHANNEL_C")){
	System.out.println("通过短信渠道C发送短信");
}

//... 此处省略部分代码 ...

如果又加其他短信渠道了呢?你又写一个“else if …" ?
显然这种做法不可取,也不符合SOLID原则中的”开闭原则“ ——对扩展开放,对更改封闭。
这样我们每次都需要修改原有代码(对更改没有封闭),不断的添加”if else"。
接下来我们把代码优化一下:

优化代码1

定义一个短信渠道的接口 SmsChannelService,所有的短信渠道API都实现该接口;

短信渠道接口 SmsChannelService.java

public interface SmsChannelService{
	//发送短信
	void send(String phoneNo,String content);
}

短信渠道A SmsChannelServiceImplA.java

public class SmsChannelServiceImplA implements SmsChannelService {
	public void send(String phoneNo, String content) {
		System.out.println("通过短信渠道A发送短信");
	}
}

短信渠道B SmsChannelServiceImplB.java

public class SmsChannelServiceImplB implements SmsChannelService {
	public void send(String phoneNo, String content) {
		System.out.println("通过短信渠道B发送短信");
	}
}

通过工厂类来初始化所有短信渠道service

SmsChannelFactory.java

public class SmsChannelFactory {
	private Map<String,SmsChannelService> serviceMap;

	//初始化工厂,将所有的短信渠道Service放入Map中
	public SmsChannelFactory(){
		//渠道类型为 key , 对应的服务类为value :
		serviceMap=new HashMap<String, SmsChannelService>(2);
		serviceMap.put("CHANNEL_A",new SmsChannelServiceImplA());
		serviceMap.put("CHANNEL_B",new SmsChannelServiceImplB());
	}

	//根据短信渠道类型获得对应渠道的Service
	public SmsChannelService buildService(String channelType){
		return serviceMap.get(channelType);
	}
}

在原来的SmsSendService中调用不同短信渠道的接口。
原来的 SmsSendService 类优化如下

public class SmsSendService {

	private SmsChannelFactory smsChannelFactory;

	public SmsSendService(){
		smsChannelFactory=new SmsChannelFactory();
	}

	public void send(String phoneNo,String content){
		//从配置中读取 短信渠道
		String channelType=config.getChannelType();
		//获取渠道类型对应的服务类
		SmsChannelService channelService=smsChannelFactory.buildService(channelType);
		//发送短信
		channelService.send(phoneNo,content);
	}

}

这样SmsSendService类非常简洁,把“if else"干掉了,
如果我要增加一个短信渠道C,无需再次更改 SmsSendService 类。
只需要增加一个类 SmsChannelServiceImplC 实现 SmsChannelService 接口,
然后在工厂类 SmsChannelFactory 中增加一行初始化 SmsChannelServiceImplC 的代码即可。

增加短信渠道C的实现 SmsChannelServiceImplC.java

public class SmsChannelServiceImplC implements SmsChannelService {
	public void send(String phoneNo, String content) {
		System.out.println("通过短信渠道C发送短信");
	}
}

修改工厂类 SmsChannelFactory.java

public class SmsChannelFactory {
	private Map<String,SmsChannelService> serviceMap;

	//初始化 serviceMap ,将所有的短信渠道Service放入Map中
	public SmsChannelFactory(){
		//渠道类型为 key , 对应的服务类为value :
		serviceMap=new HashMap<String, SmsChannelService>(3);
		serviceMap.put("CHANNEL_A",new SmsChannelServiceImplA());
		serviceMap.put("CHANNEL_B",new SmsChannelServiceImplB());
		//ADD 增加一行 SmsChannelServiceImplC 的初始化代码
		serviceMap.put("CHANNEL_C",new SmsChannelServiceImplC());
	}

	//根据渠道类型构建短信渠道Service
	public SmsChannelService buildService(String channelType){
		return serviceMap.get(channelType);
	}
}

“if else"是干掉了,但还是得修改原来的类 SmsChannelFactory ,不满足"开闭原则",有没有更好得方式呢?

我们通过使用spring的依赖注入进一步优化代码:

优化代码2

SmsChannelService 接口增加 getChannelType() 方法,这一步很关键。

public interface SmsChannelService {
	//发送短信
	void send(String phoneNo,String content);
	//关键:增加getChannelType()方法,子类实现这个方法用于标识出渠道类型
	String getChannelType();
}

子类增加该方法的实现,并加上 @Service 注解,使其让spring容器管理起来

SmsChannelServiceImplA.java

@Service
public class SmsChannelServiceImplA implements SmsChannelService {
	public void send(String phoneNo, String content) {
		System.out.println("通过短信渠道A发送短信");
	}
	//关键:增加 getChannelType() 实现
	public String getChannelType() {
		return "CHANNEL_A";
	}
}

SmsChannelServiceImplB.java

@Service
public class SmsChannelServiceImplB implements SmsChannelService {
	public void send(String phoneNo, String content) {
		System.out.println("通过短信渠道B发送短信");
	}
	//关键:增加 getChannelType() 实现
	public String getChannelType() {
		return "CHANNEL_B";
	}
}

修改 SmsChannelFactory 类: 这一步也很关键。

SmsChannelFactory.java

@Service
public class SmsChannelFactory {

	private Map<String,SmsChannelService> serviceMap;

	/*注入:通过spring容器将所有实现 SmsChannelService 接口的类的实例注入到 serviceList 中*/
	@Autowired
	private List<SmsChannelService> serviceList;

	/*通过 @PostConstruct 注解,在 SmsChannelFactory 实例化后,来初始化 serviceMap */
	@PostConstruct
	private void init(){
		if(CollectionUtils.isEmpty(serviceList)){
			return ;
		}
		serviceMap=new HashMap<String, SmsChannelService>(serviceList.size());
		//将 serviceList 转换为 serviceMap
		for (SmsChannelService channelService : serviceList) {
			String channelType=channelService.getChannelType();
			//重复性校验,避免不同实现类的 getChannelType() 方法返回同一个值。
			if(serviceMap.get(channelType)!=null){
				throw new RuntimeException("同一个短信渠道只能有一个实现类");
			}
			/*渠道类型为 key , 对应的服务类为value :
			与“优化代码1”中的通过手工设置“CHANNEL_A"、"CHANNEL_B"相比,
			这种方式更加自动化,后续在增加“CHANNEL_C"无需再改此处代码*/
			serviceMap.put(channelType,channelService);
		}
	}

	//根据渠道类型获取对应短信渠道的Service
	public SmsChannelService buildService(String channelType){
		return serviceMap.get(channelType);
	}
}

SmsSendService 加上 @Service 注解。通过 @Autowired 注入 SmsChannelFactory

SmsSendService.java

@Service
public class SmsSendService {

	@Autowired
	private SmsChannelFactory smsChannelFactory;

	public void send(String phoneNo,String content){
		//从配置中读取短信渠道类型
		String channelType=config.getChannelType();
		//构建渠道类型对应的服务类
		SmsChannelService channelService=smsChannelFactory.buildService(channelType);
		//发送短信
		channelService.send(phoneNo,content);
	}

}

这时,如果需要添加一个渠道C,那真的只需要添加一个 SmsChannelServiceImplC 即可,再也不用改原有代码,完全遵循“开闭原则”。

SmsChannelServiceImplC.java

@Service
public class SmsChannelServiceImplC implements SmsChannelService {
	public void send(String phoneNo, String content) {
		System.out.println("通过短信渠道C发送短信");
	}

	public String getChannelType() {
		return "CHANNEL_C";
	}
}

以上就是如何优雅的替换掉Java代码中的if else的详细内容,更多关于替换代码中的if else的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java通过Scanner了解if...else if语句

    这篇文章主要介绍了Java通过Scanner了解if...else if语句,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 使用Scanner类(控制台输入)来认识一下if...else if语句 if ....else if 是连续型的条件语句 判断条件有多个 的时候可以使用 使用时机:多条件 要跟划分区间一样的时候 0~59分 不及格 60~80分 不错 81~90分 很棒 91~100 成绩超级好 其余条件 都是不合法 import ja

  • Java编程细节重构之为什么if-else不是好代码详析

    前言 面向过程设计和面向对象设计的主要区别是:是否在业务逻辑层使用冗长的if else判断.如果你还在大量使用if else,当然,界面表现层除外,即使你使用Java/C#这样完全面向对象的语言,也只能说明你的思维停留在传统的面向过程语言上.本文将通过示例代码给大家介绍关于Java编程细节重构之if-else的相关内容,下面来一起看看详细的介绍吧 平时开发中if-else用的多吗? 其实这是个再正常不过的coding习惯,当我们代码量小的时候用来做条件判断是再简单不过的了. 但对于优秀程序员来说

  • Java利用策略模式优化过多if else代码

    前言 不出意外,这应该是年前最后一次分享,本次来一点实际开发中会用到的小技巧. 比如平时大家是否都会写类似这样的代码: if(a){ //dosomething }else if(b){ //doshomething }else if(c){ //doshomething } else{ ////doshomething } 条件少还好,一旦 else if 过多这里的逻辑将会比较混乱,并很容易出错. 比如这样: 摘自cim中的一个客户端命令的判断条件. 刚开始条件较少,也就没管那么多直接写的:

  • java的if else语句入门指南(推荐)

    条件语句,是程序中根据条件是否成立进行选择执行的一类语句,这类语句在实际使用中,难点在于如何准确的抽象条件.例如实现程序登录功能时,如果用户名和密码正确,则进入系统,否则弹出"密码错误"这样的提示框等. 本部分对于条件语句的介绍,重点在于语法讲解和基本的使用,更详细的使用参看后续的综合示例部分. 在Java语言中,条件语句主要有两类语法:if语句和switch语句. 1 .if语句 if关键字中文意思是如果,其细致的语法归纳来说总共有三种:if语句.if-else语句和if-else

  • Java的Struts框架中的if/else标签使用详解

    这些标签执行可在每一种语言找到的一种基本条件流程. 'If'标签可用于本身或与"Else If''标签和/或单/多'Else'标签,如下图所示: <s:if test="%{false}"> <div>Will Not Be Executed</div> </s:if> <s:elseif test="%{true}"> <div>Will Be Executed</div>

  • 聊聊java中一些减少if else 的编码习惯的方法

    前言 前段时间在阅读别人所写的代码的时候 , 发现其中一些业务相关的方法体内 , 出现了比较多的if-else语句多层嵌套的情况  . 首先我个人不是不提倡写if-else语句 , 不得不说 , 很多时候 , 在写某些逻辑 使用if-else 去做判断 , 代码看起来还是十分直观的  , 但是如果滥用if-else , 形成多层嵌套或者形成, 其中每个case 还包含了大量的逻辑 , 此时从可读性来说 , 使用if-else就有点得不偿失了 .  而且某些时候 ,  可能并不需这么多的if-el

  • 如何在java 8 stream表达式实现if/else逻辑

    简介 在Stream处理中,我们通常会遇到if/else的判断情况,对于这样的问题我们怎么处理呢? 还记得我们在上一篇文章lambda最佳实践中提到,lambda表达式应该越简洁越好,不要在其中写臃肿的业务逻辑. 接下来我们看一个具体的例子. 传统写法 假如我们有一个1 to 10的list,我们想要分别挑选出奇数和偶数出来,传统的写法,我们会这样使用: public void inForEach(){ List<Integer> ints = Arrays.asList(1, 2, 3, 4

  • java如何消除太多的if else判断示例代码

    1.简介 if判断语句是很多编程语言的重要组成部分.但是,若我们最终编写了大量嵌套的if语句,这将使得我们的代码更加复杂和难以维护. 让我们看看能否使用别的方式来做呢. 设计模式是为了更好的代码重用性,可读性,可靠性,可维护性,它有六大原则: 单一职责原则(Single Responsibility Principle,简称SRP):该原则是针对类来说的,即一个类应该只负责一项职责. 开放--封闭原则(The Open-Closed Principle简称OCP):是说软件实体(类.模块.函数等

  • 详解Java编程中if...else语句的嵌套写法

    if...else if...else语句 if语句后面可以跟elseif-else语句,这种语句可以检测到多种可能的情况. 使用if,else if,else语句的时候,需要注意下面几点: if语句至多有1个else语句,else语句在所有的elseif语句之后. If语句可以有若干个elseif语句,它们必须在else语句之前. 一旦其中一个else if语句检测为true,其他的else if以及else语句都将跳过执行. 语法 if...else语法格式如下: if(布尔表达式 1){

  • Java中if...else语句使用的学习教程

    if语句 一个if语句包含一个布尔表达式和一条或多条语句. 语法 If语句的用语法如下: if(布尔表达式) {    //如果布尔表达式为true将执行的语句 } 如果布尔表达式的值为true,则执行if语句中的代码块.否则执行If语句块后面的代码. public class Test { public static void main(String args[]){ int x = 10; if( x < 20 ){ System.out.print("这是 if 语句");

  • Java基础教程_判断语句if else

    与三元运算符相比: 好处:可以简化if else 代码 弊端 因为是一个运算符,所以运算玩必须要有一个结果 以上这篇Java基础教程_判断语句if else就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们.

随机推荐