如何少写if-else
Posted 菜鸟修仙传
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何少写if-else相关的知识,希望对你有一定的参考价值。
1.卫语句提前return
假设有如下代码
if (condition) {
// do something
} else {
return xxx;
}
通过对判断条件取反,代码在逻辑表达上会更加清晰
if (!condition) {
return xxx;
}
// do something
2.使用Optional简化if判空
2.1简化1级判空
假设有如下代码
if (input != null) {
// return value1
} else {
// return value2
}
使用Optional后
Optional.ofNullable(input).map(value1).orElse(value2);
2.2简化多级判空
假设有如下代码
if (input != null && input.getUser() != null && input.getUser().getName() != null) {
// return value1
} else {
// return value2
}
使用Optional后
return Optional.ofNullable(input)
.map(Input::getUser)
.map(User::getName)
.map(value1)
.orElse(value2);
对于没有else的场景,使用ifPresent即可
if (input != null && input.getUser() != null && input.getUser.getName() != null) {
// do action
}
Optional.ofNullable(input)
.map(Input::getUser)
.map(User::getName)
.ifPresent(action);
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();
}
这就是不要根据不同的参数类型走不同的代码逻辑,这种场景很常见,他还会以switch-case的方式出现:
switch (petType) {
case "dog":
// 处理dog
break;
case "cat":
// 处理cat
break;
case "pig":
// 处理pig
break;
case "rabbit":
// 处理rabbit
break;
default:
throw new UnsupportedOperationException();
}
不同的代码逻辑就代表了不同的策略,我们可以通过如下几个方式改写。
3.1多态
public interface Strategy {
void invoke(); // 处理各个逻辑
}
public class DogStrategy implements Strategy {
@Override
public void invoke() {
// 处理dog
}
}
public class CatStrategy implements Strategy {
@Override
public void invoke() {
// 处理cat
}
}
public class PigStrategy implements Strategy {
@Override
public void invoke() {
// 处理pig
}
}
public class RabbitStrategy implements Strategy {
@Override
public void invoke() {
// 处理rabbit
}
}
具体的策略对象可以放在一个Map中,优化后的实现类似如下
Strategy strategy = map.get(petType);
stratefy.invoke();
关于如何存放到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();
3.1.2Spring托管下的动态注册
(1) 定义一个注册中心用于接受注册信息
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);
}
}
(2) 将每个Strategy交由Spring管理,并在构造后注册
@Component
public class DogStrategy implements Strategy {
@PostConstruct
public void init() {
StrategyMapping.INSTANCE.register("dog", this.getClass());
}
@Override
public void invoke() {
// 处理dog
}
}
(3) 使用方式就变成了
Strategy strategy = StrategyMapping.INSTANCE.getStrategy(petType);
stratefy.invoke();
3.1.3Spring托管下的注解绑定
如果你不想在每个bean里面使用@PostConstruct去注册,你还可以使用注解来完成绑定并注册
(1) 先声明一个注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface StrategyMapping {
String type();
}
(2) 在bean上绑定注解
@StrategyMapping(type = "dog")
@Component
public class DogStrategy implements Strategy {
@Override
public void invoke() {
// 处理dog
}
}
(3) 增加一个监听器用于注册
@Component
public class StrategyAnnotationRegistry implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
registerStrategy(event);
}
private void registerStrategy(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
Map<String, Object> beansWithAnnotation = context.getBeansWithAnnotation(StrategyMapping.class);
beansWithAnnotation.forEach((key, value) -> {
String type = AnnotationUtils.findAnnotation(value.getClass(), StrategyMapping.class).type();
if (value instanceof Strategy) {
StrategyMapping.INSTANCE.register(type, (Strategy) value);
}
});
}
}
(4) 注册中心我们就可以不存class了,存实际的bean
public enum StrategyMapping {
INSTANCE;
private final Map<String, Strategy> map = new ConcurrentHashMap<>();
public void register(String type, Strategy bean) {
map.put(type, bean);
}
public Strategy getStrategy(String type) {
Strategy bean = map.get(type);
if (bean == null) {
throw new UnregisteredException();
}
return bean;
}
}
(5) 使用方式与之前一致
Strategy strategy = StrategyMapping.INSTANCE.getStrategy(petType);
stratefy.invoke();
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();
}
这样在调用时的代码就类似如下:
PetType petType = PetType.valueOf(type.toUpperCase(Locale.ROOT));
petType.invoke();
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();
使用方式则变成了
Runnable task = map.get(petType);
task.run();
4.责任链模式
与策略模式类似,假设我们已经将不同的业务逻辑都抽离成了单独的Strategy类,即我们有了DogStrategy
,CatStrategy
,PigStrategy
,RabbitStrategy
,在3.策略模式中,我们主要是使用了表驱动
的方式,得到了petType和Strategy的映射关系,在执行之前获取了既定的Strategy。
换一种思路,如果我们不从外部去维护这种映射关系,而是让各个Strategy自己去判断是否执行,这样只需维护一个Strategy组成的List,然后逐个请求、尝试执行即可。
不过这种方式,在Strategy较多的情况下,遍历List的性能要比表驱动差。
这种类似责任链的方式,也有两种体现方式,主要差异在于外部迭代还是内部迭代。
4.1 外部迭代
和3.1多态类似,我们定义一套策略,不过接口上有些差异
public interface Strategy {
boolean isSupport(String petType); // 是否由当前策略处理
void invoke(); // 处理各个逻辑
}
public class DogStrategy implements Strategy {
@Override
public boolean isSupport(String petType) {
return "dog".equals(petType);
}
@Override
public void invoke() {
// 处理dog
}
}
public class CatStrategy implements Strategy {
@Override
public boolean isSupport(String petType) {
return "cat".equals(petType);
}
@Override
public void invoke() {
// 处理cat
}
}
public class PigStrategy implements Strategy {
@Override
public boolean isSupport(String petType) {
return "pig".equals(petType);
}
@Override
public void invoke() {
// 处理pig
}
}
public class RabbitStrategy implements Strategy {
@Override
public boolean isSupport(String petType) {
return "rabbit".equals(petType);
}
@Override
public void invoke() {
// 处理rabbit
}
}
然后我们维护一个由Strategy组成的List,这里同样可以采用静态注册
或动态注册
的方式,这里以静态注册
为例:
List<Strategy> list = ImmutableList.<Strategy>builder()
.put(new DogStrategy())
.put(new CatStrategy())
.put(new PigStrategy())
.put(new RabbitStrategy())
.build();
调用的方式则变成了
for (Strategy strategy : list) {
if (strategy.isSupport(petType)) {
strategy.invoke();
}
}
以上是关于如何少写if-else的主要内容,如果未能解决你的问题,请参考以下文章
[原创]java WEB学习笔记61:Struts2学习之路--通用标签 property,uri,param,set,push,if-else,itertor,sort,date,a标签等(代码片段