如何减少代码中的if-else嵌套
实际项目中,往往有大量的if-else语句进行各种逻辑校验,参数校验等等,大量的if-else,语句使代码变得臃肿且不好维护,本篇文章结合我自己的经验,就减少if-else语句给出以下几种方案,分别适用于不同的场景,供大家参考,如有疑问或者建议,请大家及时指出;
//使用if-else语句 String str; if (user.getAge()>18){ str="已成年"; }else { str="未成年"; } //使用三元表达式 str=user.getAge()>18?"成年":"未成年";
//使用 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) } }
优点 :适合复杂的业务逻辑,代码可扩展性强
缺点: 通常要配合工厂模式或者享元模式使用
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!