打印
[开发工具]

改写“if-else”

[复制链接]
310|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
公羊子丹|  楼主 | 2022-9-10 19:18 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
if-else,这是个再正常不过的coding习惯,当我们代码量小的时候用来做条件判断是再简单不过的了。但对于优秀程序员来说,这却不是好代码。不信你往下看…

1. 卫语句提前return

假设有如下代码
  • if (condition) {
  •       // do something
  •   } else {
  •       return xxx;
  •   }

[color=rgb(51, 102, 153) !important]复制代码


通过对判断条件取反,代码在逻辑表达上会更加清晰
  •   if (!condition) {
  •       return xxx;
  •   }
  •   // do something

[color=rgb(51, 102, 153) !important]复制代码


2. 使用Optional简化if判空

2.1 简化1级判空
假设有如下代码
  • if (input != null) {
  •       // return value 1
  •   } else {
  •       // return value 2
  •   }

[color=rgb(51, 102, 153) !important]复制代码


使用Optional后
  • return Optional.ofNullable(input).map(value1).orElse(value2);

[color=rgb(51, 102, 153) !important]复制代码

2.2 简化多级判空
假设有如下代码
  • if (input != null && input.getUser() != null && input.getUser().getName() != null) {
  •       // do action 1
  •   } else {
  •       // do action 2
  •   }

[color=rgb(51, 102, 153) !important]复制代码


使用Optional后
  • Optional.ofNullable(input)
  •       .map(Input::getUser)
  •       .map(User::getName)
  •       .map(action1)
  •       .orElse(action2);

[color=rgb(51, 102, 153) !important]复制代码


对于没有else的场景,使用ifPresent即可
  • if (input != null && input.getUser() != null && input.getUser.getName() != null) {
  •       // do action
  •   }

[color=rgb(51, 102, 153) !important]复制代码

  • Optional.ofNullable(input)
  •       .map(Input::getUser)
  •       .map(User::getName)
  •       .ifPresent(action);

[color=rgb(51, 102, 153) !important]复制代码


3. 策略模式

假设有如下代码:
  • if ("dog".equals(petType)) {
  •       // 处理dog
  •   } else if ("cat".equals(petType)) {
  •       // 处理cat
  •   } else if ("pig".equals(petType)) {
  •       // 处理pig
  •   } else if ("rabbit".equals(petType)) {
  •       // 处理rabbit
  •   } else {
  •       throw new UnsupportedOperationException();

[color=rgb(51, 102, 153) !important]复制代码


这就是不要根据不同的参数类型走不同的代码逻辑,这种场景很常见,他还会以switch-case的方式出现:
  • switch (petType) {
  •       case "dog":
  •           // 处理dog
  •           break;
  •       case "cat":
  •           // 处理cat
  •           break;
  •       case "pig":
  •           // 处理pig
  •           break;
  •       case "rabbit":
  •           // 处理rabbit
  •           break;
  •       default:
  •           throw new UnsupportedOperationException();
  •   }

[color=rgb(51, 102, 153) !important]复制代码


不同的代码逻辑就代表了不同的策略,我们可以通过如下几个方式改写。

3.1 多态
  • public interface Strategy {
  •       void invoke(); // 处理各个逻辑
  •   }

[color=rgb(51, 102, 153) !important]复制代码

  • public class DogStrategy implements Strategy {
  •       @Override
  •       public void invoke() {
  •           // 处理dog
  •       }
  •   }

[color=rgb(51, 102, 153) !important]复制代码

  • public class CatStrategy implements Strategy {
  •       @Override
  •       public void invoke() {
  •           // 处理cat
  •       }
  •   }

[color=rgb(51, 102, 153) !important]复制代码

  • public class PigStrategy implements Strategy {
  •       @Override
  •       public void invoke() {
  •           // 处理pig
  •       }
  •   }

[color=rgb(51, 102, 153) !important]复制代码

  • public class RabbitStrategy implements Strategy {
  •       @Override
  •       public void invoke() {
  •           // 处理rabbit
  •       }
  •   }

[color=rgb(51, 102, 153) !important]复制代码


具体的策略对象可以放在一个Map中,优化后的实现类似如下
  • Strategy strategy = map.get(petType);
  •   stratefy.invoke();

[color=rgb(51, 102, 153) !important]复制代码


关于如何存放到Map中也两个可以参考的方式。

3.1.1 静态表
  • Map<String, Strategy> map = ImmutableMap.<String, Strategy>builder()
  •       .put("dog", new DogStrategy())
  •       .put("cat", new CatStrategy())
  •       .put("pig", new PigStrategy())
  •       .put("rabbit", new RabbitStrategy())
  •       .build();

[color=rgb(51, 102, 153) !important]复制代码


3.1.2 Spring托管下的动态注册
定义一个注册中心用于接受注册信息
  • public enum StrategyMapping {
  •       INSTANCE;
  •       private final Map<String, Class<? extends Strategy>> map = new ConcurrentHashMap<>();
  •       public void register(String type,  Class<? extends Strategy> clazz) {
  •           map.put(type, clazz);
  •       }
  •       public Strategy getStrategy(String type) {
  •           Class<? extends Strategy> clazz = map.get(type);
  •           if (clazz == null) {
  •               throw new UnregisteredException();
  •           }
  •           return SpringContextHolder.getBean(clazz);
  •       }
  •   }

[color=rgb(51, 102, 153) !important]复制代码


将每个Strategy交由Spring管理,并在构造后注册
  • @Component
  •   public class DogStrategy implements Strategy {
  •       @PostConstruct
  •       public void init() {
  •           StrategyMapping.INSTANCE.register("dog", this.getClass());
  •       }
  •       @Override
  •       public void invoke() {
  •           // 处理dog
  •       }
  •   }

[color=rgb(51, 102, 153) !important]复制代码


使用方式就变成了
  • Strategy strategy = StrategyMapping.INSTANCE.getStrategy(petType);
  •   stratefy.invoke();

[color=rgb(51, 102, 153) !important]复制代码

3.2 枚举
采用多态会额外产生很多策略类,如果我们已经预先将petType定义成了枚举,就会发现可以把Strategy中的invoke()方法放到枚举中,从而完成了一种映射关系。
  • public enum PetType {
  •       DOG {
  •           @Override
  •           public void invoke() {
  •               // 处理dog
  •           }
  •       },
  •       CAT {
  •           @Override
  •           public void invoke() {
  •               // 处理cat
  •           }
  •       },
  •       PIG {
  •           @Override
  •           public void invoke() {
  •               // 处理pig
  •           }
  •       },
  •       RABBIT {
  •           @Override
  •           public void invoke() {
  •               // 处理rabbit
  •           }
  •       };
  •       public abstract void invoke();
  •   }

[color=rgb(51, 102, 153) !important]复制代码


这样在调用时的代码就类似如下:
  • PetType petType = PetType.valueOf(type.toUpperCase(Locale.ROOT));
  •   petType.invoke();

[color=rgb(51, 102, 153) !important]复制代码


3.3 函数式简化策略
同样面对多态会额外产生很多策略类的问题,除了枚举我们还可以使用函数式的方式来改写,这里有个前提最好是策略的内容不会过于复杂,不然在代码的可读性上会比较差。同样我们会有一个map静态表,不过map里面存放的是lambda
  • Map<String, Runnable> map = ImmutableMap.<String, Runnable>builder()
  •       .put("dog", () -> {
  •           // 处理dog
  •       })
  •       .put("cat", () -> {
  •           // 处理cat
  •       })
  •       .put("pig", () -> {
  •           // 处理pig
  •       })
  •       .put("rabbit", () -> {
  •           // 处理rabbit
  •       })
  •       .build();

[color=rgb(51, 102, 153) !important]复制代码


使用方式则变成了
  • Runnable task = map.get(petType);
  •   task.run();



使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

49

主题

3912

帖子

1

粉丝