如何减少代码中的if-else语句,写出优雅的代码,

  • 实际项目中,往往有大量的if-else语句进行各种逻辑校验,参数校验等等,大量的if-else,语句使代码变得臃肿且不好维护,本篇文章结合我自己的经验,就减少if-else语句给出以下几种方案,分别适用于不同的场景,供大家参考,如有疑问或者建议,请大家及时指出;

一. 方案一:使用三元表达式:

//使用if-else语句
      String str;
      if (user.getAge()>18){
            str="已成年";
      }else {
            str="未成年";
      }
      //使用三元表达式
      str=user.getAge()>18?"成年":"未成年";
复制代码

优点: 简化代码,减少代码臃肿

缺点: 适用于条件比较少,逻辑判断比较简单的的情况,当if条件比较多时,代码也会过于臃肿,不利于阅读和维护

二. 方案二:使用JDK1.8中的Optional类包装

Optional类是JDK1.8的新特性之一,功能也是非常之强大,下面的例子介绍如何减少if语句

//使用 if 语句
        User user=userService.findById(userId);
        if (null==user){
            throw new RuntimeException("参数错误,未找到指定用户");
        }
        //使用Optional类包装
        Optional.ofNullable(userService.findById(userId)).orElseThrow(()->new RuntimeException("参数错误,未找到指定用户"));
复制代码

使用Optional类的好处还在于在包装成Optional容器后,可以使用函数式编程中的相关方法,例如filter(),map()方法,等等,用于筛选和转换我们业务中的逻辑和对象,使得代码得灵活性大大提高,例如:

//筛选出大于18岁的用户,如果没有就抛出异常
        Optional.ofNullable(userService.findById(userId))
                .filter(x->x.getAge()>18)
                .orElseThrow(()->new RuntimeException("参数错误,未找到指定用户"));
复制代码

代码是否简洁了很多呢

优点: 可以进行较为复杂的逻辑判断

缺点: 条件判断不宜过多,过多的条件判断下不宜使用该方式

三.方案三:使用断言Assert类

在Spring的org.springframework.util包中,内置了Assert断言类,用于条件表达式的判断

//使用断言类
        User user=userService.findById(userId);
        Assert.notNull(user, "参数错误,未找到指定用户");
复制代码

断言类中的方法返回值是void,断言类常用于我们做Junit单元测试,由于单元测试的方法均是无参数,无返回值的方法,因此Assert断言类用于测试程序的返回值是否符合我们预期是再好不过了

优点:内置了很多判断方法,例如 notNull,notEmpty,equal等方法,代码可读性强,相对方案一和方案二,可以适用于较多的判断分支;

缺点:在断言失败时,异常只能是IllegalArgumentException(message),适用于较简单的逻辑判断

四.方案四:使用@Validate注解进行入参校验的判断

在企业开发中,进行表单验证,以及接口的入参校验时,往往会使用大量的if-else语句做参数校验,这样代码会显得特别臃肿和冗余,因此我们可以使用封装好的库来进行校验,在JSR-303规范中,定义了参数校验的注解@Valid,个大框架厂商例如spring,基于JSR303规范,提供了各自的实现,并且提供了很多高级的功能,例如@Validated就是@Valid的变体;

以下摘自org.springframework.validation.annotation中@Validated注解的文档注释

Variant of JSR-303's {@link javax.validation.Valid}, supporting the specification of validation groups. Designed for convenient use with Spring's JSR-303 support but not JSR-303 specific.
//接口定义
    @RequestMapping("/update")
       public void updateUser(@RequestBody @Validated User user) {
           userService.updateUser(user);
   }
   //参数校验
   public class User implements Serializable {
       @NotNull(message = "参数不能为空")
       private Integer id;
       
       @NotBlank(message = "参数不能为空")
       private String username;
       
       @NotBlank(message = "参数不能为空")
       private String password;
       
       @NotEmpty(message = "参数不能为空")
       private List<String> desc;
       
       //这里可以通过正则来校验时间格式是否正确
       @NotNull(message = "参数不能为空")
       @Pattern(regexp = "xxxx",message ="时间格式不符合规范" )
       private Date date;
   }
复制代码

注意:如果@Validated参数校验失败,会抛出异常,如果需要在代码中接收异常,可以在接口的参数中,添加参数BindingResult,添加了这个类,之后,异常就会被封装到这个类中,不会向外抛出,我们可以调用这个类的API去获取具体的异常信息,之后,我们可以根据异常信息,去定制化我们自己的响应

public ModelAndView save(@Validated CategoryForm form,
                            BindingResult bindingResult,
                            Map<String, Object> map) {
       if (bindingResult.hasErrors()) {
           map.put("msg", bindingResult.getFieldError().getDefaultMessage());
           map.put("url", "/sell/seller/category/index");
           return new ModelAndView("common/error", map);
       }
   }
复制代码

优点: 非常适合在特定环境下做接口入参的校验

缺点: 局限性大,无法在业务逻辑中使用

五.方案五:策略模式

策略模式是设计模式之一,设计模式的初衷是为了解决代码中的特定问题而存在,百度一下策略模式的定义:

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

简单来说就是,算法(策略)和对象已经预先定义好,随传入参数的改变而选择不同的算法(策略),更加具体的语义不就是根据不用的条件(if--else),选择性的执行不同的代码吗? 本人在开发中也是屡次使用策略模式来重构复杂的if-else逻辑判断,屡试不爽,大大提高代码的优雅性;

下面是在企业开发中本人的例子,在Spring中如何使用策略模式 使用场景: 需求:多个接口,响应相同,根据参数传入的类型,使用不同的策略;

//策略上下文
  @Configuration
  public class StrategyContext{
      
      @Resource
      public Map<String,Strategy> strategyMap;
      
      public List<Resp> doGet(String type){
          Strategy strategy =strategyMap.get(type);
          retun strategy.doStrategy();
      }
  }
  //配置策略
  @Configuration
  public class StrategyConfig{
      @Resource
      public ServiceImpl1 serviceImpl1
      
      @Resource
      public ServiceImpl2 serviceImpl2
      
      @Bean
      public Map<String,Strategy> getMap(){
          Map<String,Strategy> strategyMap =new HashMap()
          strategyMap.put("1",new ServiceImpl1());
          strategyMap.put("2",new ServiceImpl2());
          return strategyMap
      }
  //策略接口类
  public interface Strategy{
      List<Resp> doStrategy();
  }
  //具体策略1
  public class ServiceImpl1 implements Strategy{
      //重写策略方法
      @Override
      publicList<Resp> doStrategy(){
          ...
      }
  }
   //具体策略2
  public class ServiceImpl2 implements Strategy{
      //重写策略方法
      @Override
      publicList<Resp> doStrategy(){
          ...
      }
  }
  //在Controller层的代码中注入策略上下文
  public class AAAController{
      @Autowired
      public StrategyContext context;
      
      public List<Resp> getXXX(String type){
          //设计模式-策略模式
          return context.doGet(type)
      }
      
  }
复制代码

优点 :适合复杂的业务逻辑,代码可扩展性强

缺点: 通常要配合工厂模式或者享元模式使用

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章