如何优雅的消除if else
一、 消除复杂if else处理逻辑
特点:复杂处理逻辑,分支多
例如多种登录方式
public interface Login {
void login();
}
@Service
public class WeiXinLogin implements Login {
@Override
public void login() {
System.out.println("===微信第三方登录===");
}
}
@Service
public class QqLogin implements Login {
@Override
public void login() {
System.out.println("===qq第三方登录===");
}
}
@Service
public class AccountLogin implements Login {
@Override
public void login() {
System.out.println("===账号密码登录===");
}
}
@Service
public class LoginService {
@Autowired
private WeiXinLogin weiXinLogin;
@Autowired
private QqLogin qqLogin;
@Autowired
private AccountLogin accountLogin;
public void toLogin(String code) {
if ("WeiXin".equals(code)) {
weiXinLogin.login();
} else if ("qq".equals(code)) {
qqLogin.login();
} else if ("account".equals(code)) {
accountLogin.login();
} else {
System.out.println("登录方式有问题");
}
}
}
有以下几种解决方案:
1. 使用注解
由于代码中通过code进行判断使用哪个登录类,只要将code与登录类绑定了,就可以不用判断了。
首先,需要定义一个注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface LoginCode {
String name();
String value();
}
在所有的登录类上都加上上述注解
@LoginCode(value = "WeiXin", name = "微信第三方登录")
@Service
public class WeiXinLogin implements Login{
@Override
public void login() {
System.out.println("===微信第三方登录===");
}
}
@LoginCode(value = "qq", name = "QQ第三方登录")
@Service
public class QqLogin implements Login{
@Override
public void login() {
System.out.println("===QQ第三方登录===");
}
}
@LoginCode(value = "account", name = "账号密码登录")
@Service
public class AccountLogin implements Login {
@Override
public void login() {
System.out.println("===账号密码登录===");
}
}
最后在调用类处理
@Service
public class LoginService2 implements ApplicationListener<ContextRefreshedEvent> {
private static Map<String, Login> loginMap = null;
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext();
Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(LoginCode.class);
loginMap = new HashMap<>();
beansWithAnnotation.forEach((key, value) ->{
String bizType = value.getClass().getAnnotation(LoginCode.class).value();
loginMap.put(bizType, (Login) value);
});
}
public void toLogin(String code) {
loginMap.get(code).login();
}
}
PayService2类实现了ApplicationListener
接口,这样在onApplicationEvent
方法中,就可以拿到ApplicationContext
的实例。
我们再获取LoginCode注解的类,放到一个map中,map中的key就是LoginCode注解中定义的value,跟code参数一致,value是登录类的实例。
这样,每次就可以每次直接通过code获取登录类实例,而不用if...else判断了。如果要加新的支付方法,只需在登录类上面打上LoginCode注解定义一个新的code即可。
注意:这种方式的code可以没有业务含义,可以是纯数字,只要不重复就行。
2. 策略+工厂模式
这种方式是用于code是有业务含义的场景。
策略模式定义了一组算法,把它们一个个封装起来, 并且使它们可相互替换。
工厂模式用于封装和管理对象的创建,是一种创建型模式。
public interface Login {
void login();
}
@Service
public class WeiXinLogin implements Login {
@PostConstruct
public void init() {
LoginStrategyFactory.register("WeiXin", this);
}
@Override
public void login() {
System.out.println("===微信第三方登录===");
}
}
@Service
public class QqLogin implements Login {
@PostConstruct
public void init() {
LoginStrategyFactory.register("qq", this);
}
@Override
public void login() {
System.out.println("===QQ第三方登录===");
}
}
@Service
public class AccountLogin implements Login {
@PostConstruct
public void init() {
LoginStrategyFactory.register("account", this);
}
@Override
public void login() {
System.out.println("===账号密码登录===");
}
}
public class LoginStrategyFactory {
private static Map<String, Login> LOGIN_REGISTERS = new HashMap<>();
public static void register(String code, Login login) {
if (null != code && !"".equals(code)) {
LOGIN_REGISTERS.put(code, login); }
}
public static Login get(String code) {
return LOGIN_REGISTERS.get(code);
}
}
@Service
public class LoginService3 {
public void toLogin(String code) {
LoginStrategyFactory.get(code).login();
}
}
这段代码的关键是LoginStrategyFactory类,它是一个策略工厂,里面定义了一个全局的map,在所有Login的实现类中注册当前实例到map中,然后在调用的地方通过LoginStrategyFactory类根据code从map获取支付类实例即可。
二、其他消除if else方式
1. 根据不同的数字或者code返回不同的字符串
public String getMessage(int code) {
if (code == 1) {
return "成功";
} else if (code == -1) {
return "失败";
} else if (code == -2) {
return "网络超时";
} else if (code == -3) {
return "参数错误";
}
throw new RuntimeException("code错误");
}
可以替换为枚举类处理该类情况
public enum MessageEnum {
SUCCESS(1, "成功"),
FAIL(-1, "失败"),
TIME_OUT(-2, "网络超时"),
PARAM_ERROR(-3, "参数错误");
private int code;
private String message;
MessageEnum(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return this.code;
}
public String getMessage() {
return this.message;
}
public static MessageEnum getMessageEnum(int code) {
return Arrays.stream(MessageEnum.values()).filter(x -> x.code == code).findFirst().orElse(null);
}
}
调用方法改为:
public String getMessage(int code) {
MessageEnum messageEnum = MessageEnum.getMessageEnum(code);
return messageEnum.getMessage();
}
2. 集合循环中的判断
上面的枚举MessageEnum中的getMessageEnum方法,如果不用java8的语法的话,可能要这样写
public static MessageEnum getMessageEnum(int code) {
for (MessageEnum messageEnum : MessageEnum.values()) {
if (code == messageEnum.code) {
return messageEnum;
}
}
return null;
}
对于集合中过滤数据,或者查找方法,java8有更简单的方法消除if...else判断。
public static MessageEnum getMessageEnum(int code) {
return Arrays.stream(MessageEnum.values()).filter(x -> x.code == code).findFirst().orElse(null);
}
3. 非空检查
Java 从 8 开始引入了 Optional 类,用于表示可能为空的对象。这个类提供了很多方法,用于相关的操作,可以用于消除 if...else。
String str = "Hello World!";
if (str != null) {
System.out.println(str);
} else {
System.out.println("Null");
}
使用 Optional 之后:
Optional<String> strOptional = Optional.of("Hello World!");
strOptional.ifPresentOrElse(System.out::println, () -> System.out.println("Null"));
Optional 还有很多方法,这里不一一介绍了。但请注意,不要使用 get()
和 isPresent()
方法,否则和传统的 if...else 无异。
4. spring中的判断
对于参数的异常,越早被发现越好,在spring中提供了Assert用来帮助我们检测参数是否有效。
public void save(Integer code,String name) {
if(code == null) {
throw Exception("code不能为空");
} else {
if(name == null) {
throw Exception("name不能为空");
} else {
System.out.println("doSave");
}
}
}
如果参数非常多的话,if...else语句会很长,这时如果改成使用Assert
类判断,代码会简化很多:
public String save2(Integer code,String name) {
Assert.notNull(code,"code不能为空");
Assert.notNull(name,"name不能为空");
System.out.println("doSave");
}
5. 简单的判断
其实有些简单的if...else完全没有必要写,可以用三目运算符代替,比如这种情况:
public String getMessage2(int code) {
if(code == 1) {
return "成功";
}
return "失败";
}
改成三目运算符
public String getMessage2(int code) {
return code == 1 ? "成功" : "失败";
}
三、解决if...else 嵌套过深
if...else 多通常并不是最严重的的问题。有的代码 if...else 不仅个数多,而且 if...else 之间嵌套的很深,也很复杂,导致代码可读性很差,自然也就难以维护。
如下:
if (condition1) {
action1();
if (condition2) {
action2();
if (condition3) {
action3();
if (condition4) {
action4();
}
}
}
}
解决方案:
1. 抽取方法
重构前:
public void add(Object element) {
if (!readOnly) {
int newSize = size + 1;
if (newSize > elements.length) {
Object[] newElements = new Object[elements.length + 10];
for (int i = 0; i < size; i++) {
newElements[i] = elements[i];
}
elements = newElements
}
elements[size++] = element;
}
}
重构后:
public void add(Object element) {
if (readOnly) {
return;
}
if (overCapacity()) {
grow();
}
addElement(element);
}
2. 卫方法
重构前:
public double getPayAmount() {
double result;
if (_isDead) result = deadAmount();
else {
if (_isSeparated) result = separatedAmount();
else {
if (_isRetired) result = retiredAmount();
else result = normalPayAmount();
};
}
return result;
}
重构后:
public double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
}
参考文档: