程序员子龙(Java面试 + Java学习) 程序员子龙(Java面试 + Java学习)
首页
学习指南
工具
开源项目
技术书籍

程序员子龙

Java 开发从业者
首页
学习指南
工具
开源项目
技术书籍
  • 基础

    • java 学习路线图
    • HashMap 详解
    • Java 8 日期和时间 - 如何获取当前时间和时间戳?
    • Java 模板变量替换(字符串、占位符替换)
    • JDK 代理
    • Java SPI 详解
    • java stream 看这一篇文章就够了
    • Java 泛型详解
    • Java 动态修改注解值
    • 如何正确遍历删除List中的元素
    • 什么是Java注解
    • 异步编程神器:CompletableFuture详解
    • FIFO 、LRU、LFU算法
    • 十进制转十六进制
    • java中double类型数据加减乘除操作精度丢失问题及解决方法
    • JAVA利用反射清除实体类对应字段
    • JSON转换问题最全详解(json转List,json转对象,json转JSONObject)
    • java 8 List 根据某个字段去重
    • Java List排序
    • 压缩算法:字符串(JSON)压缩和解压【JDK之Deflater压缩与Inflater解压】
    • BCD码是什么?
    • Java二进制、八进制、十进制、十六进制转换
    • Java String字符串 与 ASCII码相互转换
    • 什么是跨域?解决方案有哪些?
    • Java 16进制字符串转10进制
    • LinkedHashMap实现LRU - 附重点源码解析
    • 去掉 if...else 的七种绝佳之法
      • 方法一:提前 return
      • 方法二:枚举
      • 方案三:Optional 判空
      • 方案四:Function
        • 处理抛出异常
        • 处理 if...else 分支
      • 方案五:表驱动法
      • 方案六:策略模式 + 工厂方法
      • 方案七:责任链模式
      • 总结
    • 一眼看清@JSONField注解使用与效果
  • JVM

  • Spring

  • 并发编程

  • Mybatis

  • 网络编程

  • 数据库

  • 缓存

  • 设计模式

  • 分布式

  • 高并发

  • SpringBoot

  • SpringCloudAlibaba

  • Nginx

  • 面试

  • 生产问题

  • 系统设计

  • 消息中间件

  • Java
  • 基础
程序员子龙
2024-03-18
目录

去掉 if...else 的七种绝佳之法

如果在IT行业的时间够长的话,可能还记得大约10几年前,设计模式风靡一时的时候,有过一段反 "if" 的运动。

所谓的反"if"运动,其实是夸大了"if"语句带来的问题,比如当时提出的问题有:

  1. 代码不好维护,特别是if或者else中的代码比较多的时候
  2. if和 else if分支太多的时候,代码难以阅读和修改
  3. 阅读含有if的代码时,必须在自己的头脑中模拟执行,会消耗你的精神能量
  4. ... ... 等等

这些问题确实存在,但是因为这些就彻底禁止if的话,就过于极端,因噎废食了。 代码中分支和循环是不可避免的,完全禁止if之后,在某些时候会产生了更加复杂和令人发指的代码, 所以,最后这个反"if"的运动也不了了之,慢慢消亡了。

今天总结一下,有哪些方法可以干掉 if...else。

# 方法一:提前 return

假如有如下代码:

if (condition){
    //todo

} else {
  return;
}
1
2
3
4
5
6

这种代码我们一般采用提前 return 的方式,去掉不必要的 else。

if (!condition){
  return
}

//todO
1
2
3
4
5

这种方法一般只适合分支结构很简单的 if...else,把一些不必要的 if...else ,去掉个人不推荐。

# 方法二:枚举

枚举其实也是可以去掉 if...else 的,如下:

String orderStatusText = null;
if ("1".equals(orderStatus)) {
    orderStatusText = "订单未支付";
} else if ("2".equals(orderStatus)) {
    orderStatusText = "订单已支付";
} else if ("3".equals(orderStatus)) {
    orderStatusText = "订单已发货";
} 
...
1
2
3
4
5
6
7
8
9

这种类型的代码非常适合枚举来解决。

先定义一个枚举类:

@Data
public enum OrderStatusEnum {
    UN_PAY("1","订单未支付"),
    PAIDED("2","订单已支付"),
    SENDED("3","订单已发货"),
    SINGED("4","订单已签收"),
    EVALUATED("5","订单已评价");

    private String status;

    private String statusDes;

    static OrderStatusEnum of(String status) {
        for (OrderStatusEnum statusEnum : OrderStatusEnum.values()) {
            if (statusEnum.getStatus().equals(status)) {
                return statusEnum;
            }
        }
        return null;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

有了这个枚举,上面代码直接可以优化为一行代码:

String orderStatusDes = OrderStatusEnum.of(orderStatus).getStatusDes();
1

这种方式也比较优雅!

# 方案三:Optional 判空

各位小伙伴的项目里面一定存在非空判断,如果为空,则抛出异常或者 return。

Order order = getById(id);
if (order == null) {
    return -1;
} else {
    return order.getOrderStatus();
}
1
2
3
4
5
6

对于这种代码我们利用 Optional 可以非常优雅地解决。

return Optional.ofNullable(order).map(o -> o.getOrderStatus()).orElse("-1");
1

这种方式是不是非常简洁。

# 方案四:Function

Function 是 Java 8 中的函数式接口,利用好它我们可以极大地简化我们的代码,比如有下面一段代码:

// 抛出异常
if (...) {
  throw new RuntimeException("出现异常...")
}

// if...else 分支
if(...) {
  doSomething1();
} else {
  doSomething2();
}
1
2
3
4
5
6
7
8
9
10
11

现在我们利用 Function 来处理上面两段代码

# 处理抛出异常

  • 定义抛出异常的形式的函数式接口
@FunctionalInterface
public interface ThrowExceptionFunction {

    /**
     * 抛出异常
     * @param message
     */
    void throwMessage(String message);
}
1
2
3
4
5
6
7
8
9

这里只需要有一个这样的函数式接口就行,而且方法也没有返回值,是一个消费型接口。

  • 增加判断工具类
public class ValidateUtils {

    /**
     * 抛出异常
     * @param flag
     * @return
     */
    public static ThrowExceptionFunction isTrue(Boolean flag) {
        return (errorMessage) -> {
            if (flag) {
                throw new RuntimeException(errorMessage);
            }
        };
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

ValidateUtils 类也是非常简单的,如果传入的 flag 为 true,则抛出异常。isTrue() 返回值也是刚刚我们定义的 ThrowExceptionFunction。

  • 使用
ValidateUtils.isTrue(flag).throwMessage("出现异常...");
1

# 处理 if...else 分支

其实使用 Function 来去掉 if...else 分支我认为有点儿偏门,因为它非常依赖我们定义的 Function 函数,比如我们定义的方法只有两个参数,那它就只能处理处理两个分支的,对于三个分支的 if...else 则需要重新定义方法。下面以两个分支为例。

  • 定义函数式接口
@FunctionalInterface
public interface ActionHandler {
    void doActionHandler(ActionService trueActionService,ActionService falseActionService);
}
1
2
3
4

函数式接口中定义了一个方法,doActionHandler(),它有两个参数,分别为:

  1. trueActionService:为 true 时要进行的操作
  2. falseActionService:为 false 时要进行的操作
  • 定义判断方法

增加一个工具类,用来判断为 true 时执行哪个方法,为 false 时执行哪个方法。

public class BussHandlerUtils {

    public static ActionHandler isTrue(Boolean flag) {
        return (trueActionService,falseActionService) -> {
            if (flag) {
                trueActionService.doAction();
            } else {
                falseActionService.doAction();
            }
        };
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
  • 使用
BussHandlerUtils.isTrue(true)
        .doActionHandler(() -> {
            //todo
        },() ->{
            //todo
    });
1
2
3
4
5
6

# 方案五:表驱动法

表驱动法,是一种让你可以在表中查找信息,而不必用过多的 if...else 来把他们找出来的方法。如下:

if ("code1".equals(action)) {
    doBuss1();
} else if ("code2".equals(action)) {
    doBuss2();
} else if ("code3".equals(action)) {
    doBuss3();
} 
1
2
3
4
5
6
7

优化方法如下:

Map<String, Function<?> action> actionMap = new HashMap<>();
action.put("code1",() -> {doBuss1()});
action.put("code2",() -> {doBuss2()});
action.put("code3",() -> {doBuss3()});

// 使用
actionMap.get(action).apply();
1
2
3
4
5
6
7

其实这种方式也不是很好,因为它会显得代码非常臃肿。一种变形方案是将 doBuss() 抽象成类。如下:

//1. 先定义一个 BussService 接口
public interface BussService {
    void doAction();
}

//2. 然后定义 3 个实现类
public class BussService1 implements BussService{
    public void doAction() {
        //todo
    }
}

//3. 加入表中
Map<String, ActionService> bussMap = new HashMap<>();
bussMap.put("code1",new BussService1());
bussMap.put("code2",new BussService2());
bussMap.put("code3",new BussService3());

//4. 调用
actionMap.get(action).doAction();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

这种方式是不是比较优雅些!

# 方案六:策略模式 + 工厂方法

策略模式 + 工厂方法是解决 if...else 用得非常多的方案,它和上面的表驱动法有点儿类似。使用策略模式 + 工厂方法分为几个步骤,以上面例子为例:

  • 把条件模块抽象为一个公共的接口,策略接口
public interface BussService {
    void doAction();
}
1
2
3
  • 根据每个逻辑,定义出自己具体的策略实现类,如下:
public class BussService1 implements BussService{
    public void doAction() {
        //todo 
    }
}

public class BussService2 implements BussService{
    public void doAction() {
        //todo
    }
}

// 省略其他策略
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 工厂类,统一调度,用来管理这些策略,如下:
public class BussServiceFactory {
    private BussServiceFactory(){

    }

    private static class SingletonHolder{
        private static BussServiceFactory instance = new BussServiceFactory();
    }

    public static BussServiceFactory getInstance(){
        return SingletonHolder.instance;
    }

    private static final Map<String,BussService> ACTION_SERVICE_MAP = new HashMap<String, BussService>();

    static {
        ACTION_SERVICE_MAP.put("action1",new BussService1());
        ACTION_SERVICE_MAP.put("action2",new BussService2());
        ACTION_SERVICE_MAP.put("action3",new BussService3());
    }

    public static BussService getActionService(String actionCode) {
        BussService bussService = ACTION_SERVICE_MAP.get(actionCode);
        if (bussnService == null) {
            throw new RuntimeException("非法 actionCode");
        }
        return actionService;
    }

    public void doAction(String actionCode) {
        getBussService(actionCode).doAction();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

单例模式实现工厂类。

  • 使用
ActionServiceFactory.getInstance().doAction("action1");
1

这种优化方式也是很优雅的,特别适合分支较多,逻辑较为复杂的代码块,这种方式将分支逻辑与业务代码解耦了,是一种很不错的方案,个人比较推荐。

# 方案七:责任链模式

责任链我们可以看做是一个单链表的数据结构,一个对象一个对象地过滤条件,符合的就执行,然后结束,不符合的就传递到下一个节点,如果每个对象都无法处理,一般都有一个最终的节点来统一处理。

我们依然以上面那个例子为例。

  • 定义责任链处理请求节点
public abstract class BussHandler {

    // 后继节点
    protected ActionHandler successor;

    /**
     * 处理请求
     * @param actionCode
     */
    public void handler(String actionCode) {
        doHandler(actionCode);
    }

    // 设置后继节点
    protected ActionHandler setSuccessor(ActionHandler successor) {
        this.successor = successor;
        return this;
    }

    // 处理请求
    public abstract void doHandler(String actionCode);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  • 定义首尾节点,用于一些异常情况的处理
// 首节点,判断 actionCode 是否为空
public class HeadHandler extends BussHandler{

    @Override
    public void doHandler(String actionCode) {
        if (StringUtils.isBlank(actionCode)) {
            throw new RuntimeException("actionCode 不能为空");
        }

        successor.doHandler(actionCode);
    }
}

// 尾节点,直接抛出异常,因为到了尾节点说明当前 code 没有处理
public class TailHandler extends BussHandler{

    @Override
    public void doHandler(String actionCode) {
        throw new RuntimeException("当前 code[" + actionCode + "] 没有具体的 Handler 处理");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  • 定义各个节点具体的实现节点
public class BussHandler1 extends BussHandler{

    @Override
    public void doHandler(String actionCode) {
        if ("action1".equals(actionCode)) {
            doAction1();
        } else {
            // 传递到下一个节点
            successor.doHandler(actionCode);
        }
    }
}

public class BussHandler2 extends BussHandler{

    @Override
    public void doHandler(String actionCode) {
        if ("action2".equals(actionCode)) {
            doAction2();
        } else {
            // 传递到下一个节点
            successor.doHandler(actionCode);
        }
    }
}

// 省略其他节点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  • 定义工厂,来构建一条完整的责任链,并负责调度
public class BussHandlerFactory {
    
    private BussHandler headHandler;
    
    private BussHandlerFactory(){
        headHandler = new HeadHandler();
        ActionHandler actionHandler1 = new ActionHandler1();
        ActionHandler actionHandler2 = new ActionHandler2();
        ActionHandler actionHandler3 = new ActionHandler3();

        BussHandler tailHandler = new TailHandler();
        
        // 构建一条完整的责任链
        headHandler.setSuccessor(actionHandler1).setSuccessor(actionHandler2).setSuccessor(actionHandler3).
                setSuccessor(actionHandler4).setSuccessor(actionHandler5).setSuccessor(tailHandler);
    }

    private static class SingletonHolder{
        private static ActionHandlerFactory instance=new ActionHandlerFactory();
    }

    public static ActionHandlerFactory getInstance(){
        return SingletonHolder.instance;
    }
        
    public void doAction(String actionCode) {
        headHandler.doHandler(actionCode);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  • 使用
BussHandlerFactory.getInstance().doAction("action1");
1

# 总结

今天在这里总结了 7 中方式用来解决 if...else 的问题,七种方案各有优劣,各自有各自的使用场景,根据使用场景选择最优的方案。

上次更新: 2024/10/09, 22:27:53
LinkedHashMap实现LRU - 附重点源码解析
一眼看清@JSONField注解使用与效果

← LinkedHashMap实现LRU - 附重点源码解析 一眼看清@JSONField注解使用与效果→

最近更新
01
一个注解,优雅的实现接口幂等性
11-17
02
MySQL事务(超详细!!!)
10-14
03
阿里二面:Kafka中如何保证消息的顺序性?这周被问到两次了
10-09
更多文章>
Theme by Vdoing | Copyright © 2024-2024

    辽ICP备2023001503号-2

  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式