如何优雅的消除if else

629 阅读4分钟

如何优雅的消除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();
}

参考文档:

www.cnblogs.com/aligege/p/1…

mp.weixin.qq.com/s/dhOltmJ7s…