代码整洁之道之优化if-else的8种方案

我们日常开发的项目中,如果代码中存在大量的if-else语句,阅读起来非常的折磨(直接劝退),维护起来也很难,也特别容易出问题。比如说以下:

接下来,本文介绍我们常使用的8种方法去优化if-else。

1、提前return,让正常流程走主干

如果if-else代码中包含return语句,或者我们可以将包含if-else的代码从主干中抽取到一个单独方法,这样就可以在这个方法中可以return掉。这中思想也是短路求值的一种体现。把多余 else 干掉,使代码更加优雅。

  • 优化前代码:
// 主流程代码  
if (condition){  
    // doSomething  
}else {  
    return;  
}
// 主流程代码  
if (condition){  
// doSomething1  
}else {  
// doSomething2  
}
  • 优化后代码:
// 主流程代码  
if (!condition){  
    return;  
}  
 // doSomething
// 主流程代码  
//doSomething  
doSomething();

private static void doSomething(){  
    // 主流程代码  
    if (!condition){  
    // doSomething1  
    return;  
    }  
    // doSomething2  
}

2、使用三目运算符

某些if-else可以优化为使用三目运算符,这样会让代码更简洁,可读性高。

  • 优化前
int price;  
if (condition1){  
    price = 1;  
} else if (condition2) {  
    price = 2;  
}else {  
    price = 0;  
}
  • 优化后
int price = condition1 ? 1 : (condition2 ? 2 : 0);

3、使用Optional

我们在代码中判null会导致存在大量的if-else,这个时候我们可以考虑使用Java8的Optional去优化。

  • 优化前
public static void main(String[] args) {  
    String s = handleStr("11");  
    System.out.println(s);  
}  
  
private static String handleStr(String str){  
    if (str != null){  
    return str.concat("。。。。。");  
    }else {  
    return "Null";  
    }  
}
  • 优化后代码:
public static void main(String[] args) {
    String s = Optional.ofNullable(handleStr("11"))
                       .map(str -> str.concat("。。。。。"))
                       .orElse("Null");

    System.out.println(s);
}

private static String handleStr(String str) {
    // 其余业务逻辑,同样适用于处理一个对象,判null
    return str;
}

4、多态

我们可以将一些操作(比如一些状态)的一些共性的方法抽象成一个公共接口,然后针对这些操作实现这些接口完成不同的逻辑,在调用时我们只需要传入对应的操作类即可,对外的操作方法都是同一个。

  • 优化前代码
public class OrderProcessing {
    public static void main(String[] args) {
        processOrder("pending");
        processOrder("paid");
        processOrder("shipped");
    }

    private static void processOrder(String status) {
        if ("pending".equalsIgnoreCase(status)) {
            System.out.println("Handling payment for pending order.");
            // 处理支付逻辑
            System.out.println("Payment handled.");
            System.out.println("Cannot ship. Payment pending.");
        } else if ("paid".equalsIgnoreCase(status)) {
            System.out.println("Payment already received.");
            System.out.println("Handling shipping for paid order.");
            // 处理发货逻辑
            System.out.println("Order shipped.");
        } else if ("shipped".equalsIgnoreCase(status)) {
            System.out.println("Payment already received.");
            System.out.println("Order already shipped.");
        } else {
            System.out.println("Invalid order status: " + status);
        }
    }
}

  • 优化后代码
// 状态接口
interface OrderState {
    void handlePayment();

    void handleShipping();
}

// 具体状态类
class PendingPaymentState implements OrderState {
    @Override
    public void handlePayment() {
        System.out.println("Payment handled for pending order.");
    }

    @Override
    public void handleShipping() {
        System.out.println("Cannot ship. Payment pending.");
    }
}

class PaidState implements OrderState {
    @Override
    public void handlePayment() {
        System.out.println("Payment already received.");
    }

    @Override
    public void handleShipping() {
        System.out.println("Shipping handled for paid order.");
    }
}

class ShippedState implements OrderState {
    @Override
    public void handlePayment() {
        System.out.println("Payment already received.");
    }

    @Override
    public void handleShipping() {
        System.out.println("Order already shipped.");
    }
}

// 上下文类
class Order {
    private OrderState currentState;

    public Order(OrderState initialState) {
        this.currentState = initialState;
    }

    public void handlePayment() {
        currentState.handlePayment();
    }

    public void handleShipping() {
        currentState.handleShipping();
    }

    public void setState(OrderState newState) {
        this.currentState = newState;
    }
}

public class StatePatternExample {
    public static void main(String[] args) {
        Order order = new Order(new PendingPaymentState());

        order.handlePayment();
        order.handleShipping();

        order.setState(new PaidState());

        order.handlePayment();
        order.handleShipping();

        order.setState(new ShippedState());

        order.handlePayment();
        order.handleShipping();
    }
}

5、枚举

对一些创建了枚举值,针对不同的枚举值有不同的操作时,枚举也可以消除if-else。个人感觉有点像策略模式或者表驱动。

  • 优化前
enum OperateTypeEnum{  
    PO(1),  
    PR(2),  
    DC_INBOUND(3),  
    DC_OUTBOUND(4);  
      
    public final Integer code;  
      
    OperateTypeEnum(Integer code) {  
        this.code = code;  
    }  
}

private static Long getOperator(Integer operator){  
    if (OperateTypeEnum.PO.code.equals(operator)){  
        return getPoOperator();  
    } else if (OperateTypeEnum.PR.code.equals(operator)) {  
        return getPrOperator();  
    } else if (OperateTypeEnum.DC_INBOUND.code.equals(operator)) {  
        return getDcInboundOperator();  
    } else if (OperateTypeEnum.DC_OUTBOUND.code.equals(operator)) {  
        return getDcOutboundOperator();  
    }else {  
        return null;  
    }  
}

private static Long getPoOperator(){return 1L;}  
  
private static Long getPrOperator(){return 2L;}  
  
private static Long getDcInboundOperator(){return 3L;}  
  
private static Long getDcOutboundOperator(){return 4L;}  
  
private static Long getDwInboundOperator(){return 5L;}  
  
private static Long getDwOutboundOperator(){return 6L;}

  • 优化后的代码
enum OperateTypeEnum{  
    PO(1){  
    @Override  
    protected Long getOperator() {  
            return 1L;  
        }  
    },  
    PR(2){  
    @Override  
    protected Long getOperator() {  
            return 2L;  
        }  
    },  
    DC_INBOUND(3){  
    @Override  
    protected Long getOperator() {  
            return 3L;  
        }  
    },  
    DC_OUTBOUND(4){  
    @Override  
    protected Long getOperator() {  
            return 4L;  
        }  
    };  
      
    public final Integer code;  
      
    OperateTypeEnum(Integer code) {  
        this.code = code;  
    }  
      
    public static OperateTypeEnum ofCode(Integer code){  
        return Arrays.stream(OperateTypeEnum.values())
        .filter(e -> e.code.equals(code))
        .findFirst().orElseThrow(() -> new RuntimeException("出错了"));  
    }  
      
    /**  
    * 定义一个公共方法  
    */  
    protected abstract Long getOperator();  
}

private static Long getOperator(Integer operatorType){  
    OperateTypeEnum operateTypeEnum = OperateTypeEnum.ofCode(operatorType);  
    return operateTypeEnum.getOperator();  
}

这种方式也是我再处理一些枚举时,对应不同的处理逻辑时常用的一种方式。比如根据订单类型的不同返回类型对应的数据。当然我们枚举方法中处理逻辑时如果想用bean的话,可以当做参数传入或者直接从Spring容器中获取。

6、表驱动(Map+函数方法)

表驱动编程是一种通过查找表格而不是嵌套条件语句来实现的编程方法。可以使用数据结构(如数组、Map)来存储条件和对应的操作,这样就不必用很多的逻辑语句(if 或 case)来把它们找出来的方法。

  • 优化前代码
private static OrderInfoVO operateOrder(String orderType, OrderInfoDO orderInfo){  
    if (orderType.equals("PO")){  
        return handlePoOrder(orderInfo);  
    }else if (orderType.equals("INBOUND")){  
        return handleInboundOrder(orderInfo);  
    } else if (orderType.equals("OUTBOUND")) {  
        return handleOutboundOrder(orderInfo);  
    }  
}
  • 优化后代码
private static OrderInfoVO handlerOrder(String orderType, OrderInfoDO orderInfo){  
    Map<String, Function<OrderInfoDO, OrderInfoVO>> functionMap = Maps.newHashMap();  
    functionMap.put("PO", (orderInfo1) -> { return handlePoOrder(orderInfo1);});  
    functionMap.put("INBOUND", (orderInfo1) -> { return handleInboundOrder(orderInfo1);});  
    functionMap.put("OUTBOUND", (orderInfo1) -> { return handleOutboundOrder(orderInfo1);});  
      
    return functionMap.get(orderType).apply(orderInfo);  
}

当然Funtion也可以根据业务需要,可以为ConsumerPredicate等。
不过我们也可以利用Spring的依赖注入,将其转换为对应的Map<String, Bean>。比如上述代码也可以这么优化:

interface IOrderHandler{  
    /**  
    * 定义统一的处理接口  
    * @param orderInfo  
    * @return  
    */  
    OrderInfoVO handlerOrder(OrderInfoDO orderInfo);  
}  
  
@Component("PO")  
class PoOrderHandler implements IOrderHandler{  
    /**  
    * 处理Po  
    *  
    * @param orderInfo  
    * @return  
    */  
    @Override  
    public OrderInfoVO handlerOrder(OrderInfoDO orderInfo) {  
        return null;  
    }  
}  
  
@Component("INBOUND")  
class InboundOrderHandler implements IOrderHandler{  
    /**  
    * 处理Inbound  
    *  
    * @param orderInfo  
    * @return  
    */  
    @Override  
    public OrderInfoVO handlerOrder(OrderInfoDO orderInfo) {  
        // 具体处理逻辑  
        return null;  
    }  
}  
  
@Component("OUTBOUND")  
class InboundOrderHandler implements IOrderHandler{  
    /**  
    * 处理Outbound  
    *  
    * @param orderInfo  
    * @return  
    */  
    @Override  
    public OrderInfoVO handlerOrder(OrderInfoDO orderInfo) {  
        return null;  
    }  
}

public class OrderSerivceImpl implements IOrderService{

    @Autowired  
    private Map<String, IOrderHandler> orderHandlerMap;

    public OrderInfoVO handleOrderInfo(String orderType, OrderInfoDO orderInfo){
        IOrderHandler orderHandler = orderHandlerMap.get(orderType);  
        return orderHandler.handlerOrder(orderInfo);
    }
}

7、策略模式+工厂模式

我们可以使用策略模式将每个条件分支抽象为一个策略类,然后在主逻辑中使用策略类来执行相应的逻辑。这种方式可以降低代码的耦合性,使得代码更加可维护和可扩展。然后再使用工厂模式定义一个策略工厂类去管理这些策略,即对外提供的都是策略工厂的方法。这种方法可以有效的去除if-else,并且代码逻辑更容易阅读维护以及扩展。
比如上例中,我们在处理不同订单类型时的handler类就是一个个的策略,我们也可以创建一个策略工厂类。

publid class OrderHandlerFactory{
    private static final Map<String, IOrderHandler> orderHandlerMap = Maps.newHashMap();  
  
static {  
    orderHandlerMap.put("PO", new PoOrderHandler());  
    orderHandlerMap.put("INBOUND", new InboundOrderHandler());  
    orderHandlerMap.put("OUTBOUND", new OutboundOrderHandler());  
}  

/**
* 获取具体处理的类
*/
public static IOrderHandler getOrderHandler(String orderType){  
return orderHandlerMap.get(orderType);  
}
}

8、规则引擎

使用规则引擎来管理条件和对应的执行逻辑。例如,Drools 是一个强大的规则引擎,它允许你定义规则并动态执行它们。再比如LiteFlow,EasyRule,都可以通过管理条件和对应的执行逻辑。可以消除if-else。规则引擎适合处理复杂的业务逻辑。通过编排条件去处理业务逻辑。后续会分享规则引擎相关的干货。

总结:

上述方案都可以达到优化if-else的效果,但是采用那种方案还是要看具体的代码逻辑以及业务处理逻辑,重要的是要评估项目的复杂性、维护性和性能需求,选择最适合项目需求的优化方案。在实际开发中,通常会根据具体情况结合多种方式来达到更好的优化效果。

本文已收录于我的个人博客:码农Academy的博客,专注分享Java技术干货,包括Java基础、SpringBoot、SpringCloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容