防禦性程式設計失敗,我開始最佳化我寫的多重 if-else 程式碼

帶你聊技術發表於2024-01-17

來源:Lorin 洛林

前言

  • • 最近防禦性程式設計比較火,不信邪的我在開發中進行了嘗試,然後我寫下了如下的程式碼:

    public static void main(String[] args) {
        // do something
        if ("滿足條件A") {
            // 查詢許可權
            if ("是否具備許可權A" && "是否具備許可權B") {
                // 查詢配置
                if ("配置是否開啟"){
                    // do something
                }
            }
        }
        // do something
    }
  • • 不出意外我被逮捕了,組內另外一位同事對我的程式碼進行了 CodeReview,我的防禦性程式設計程式設計沒有幸運逃脫,被標記上了“多重 if-else ”需要進行最佳化,至此我的第一次防禦性程式設計失敗,開始了最佳化多重 if-else 之路,下面是我總結出的常用幾種最佳化方式。

版本

  • • Java8

幾種常用的最佳化方式

提前使用 return 返回去除不必要的 else

  • • 如果我們的程式碼塊中需要使用 return 返回,我們應該儘可能早的使用 return 返回而不是使用 else

  • • 最佳化前

    private static boolean extracted(boolean condition) {
        if (condition) {
            // do something
            return false;
        }else {
            // do something
            return true;
        }
    }
  • • 最佳化後

    private static boolean extracted(boolean condition) {
        if (condition) {
            // do something
            return false;
        }
        
        // do something
        return true;
    }

使用三目運運算元

  • • 一些簡單的邏輯我們可以使用三目運運算元替代 if-else ,這樣可以讓我們的程式碼更加簡潔

  • • 最佳化前

        int num = 0;
        if (condition) {
            num = 1;
        } else {
            num = 2;
        }
  • • 最佳化後

int num = condition ? 1 : 2;

使用列舉

  • • 在某一些場景我們也可以使用列舉來最佳化多重 if-else 程式碼,使我們的程式碼更加簡潔、具備更多的可讀性和可維護性。

  • • 最佳化前

        String OrderStatusDes;
        if (orderStatus == 0) {
            OrderStatusDes = "訂單未支付";
        } else if (orderStatus == 1) {
            OrderStatusDes = "訂單已支付";
        } else if (orderStatus == 2) {
            OrderStatusDes = "已發貨";
        } else {
            throw new Exception("Invalid order status");
        }
  • • 最佳化後

public enum OrderStatusEnum {
    UN_PAID(0"訂單未支付"),
    PAIDED(1"訂單已支付"),
    SENDED(2"已發貨"),
    ;

    private final int code;
    private final String desc;

    public int getCode() {
        return code;
    }

    public String getDesc() {
        return desc;
    }

    OrderStatusEnum(int index, String desc) {
        this.code = index;
        this.desc = desc;
    }

    public static OrderStatusEnum getOrderStatusEnum(int orderStatusCode) {
        for (OrderStatusEnum statusEnum : OrderStatusEnum.values()) {
            if (statusEnum.getCode() == orderStatusCode) {
                return statusEnum;
            }
        }
        return null;
    }
}


// 當然你需要根據業務場景對異常值做出合適的處理
OrderStatusEnum.getOrderStatusEnum(2)

抽取條件判斷作為單獨的方法

  • • 當我們某個邏輯條件判斷比較複雜時,可以考慮將判斷條件抽離為單獨的方法,這樣可以使我們主流程邏輯更加清晰

  • • 最佳化前

        // do something
        if ("滿足條件A" && "滿足條件B") {
            // 查詢許可權
            if ("是否具備許可權A" && "是否具備許可權B") {
                // do something
            }
        }
        // do something
  • • 最佳化後

    public static void main(String[] args) {
        // do something
        if (hasSomePermission()) {
            // do something
        }
        // do something
    }

    private static boolean hasSomePermission() {
        if (!"滿足條件A" || !"滿足條件B") {
            return false;
        }
        // 查詢許可權
        return "是否具備許可權A" && "是否具備許可權B";
    }

有時候 switch 比 if-else 更加合適

  • • 當條件為清晰的變數和列舉、或者單值匹配時,switch 比 if-else 更加合適,可以我們帶好更好的可讀性以及更好的效能 O(1)

  • • 最佳化前

if (day == Day.MONDAY) {
    // 處理星期一的邏輯
else if (day == Day.TUESDAY) {
    // 處理星期二的邏輯
else if (day == Day.WEDNESDAY) {
    // 處理星期三的邏輯
else if (day == Day.THURSDAY) {
    // 處理星期四的邏輯
else if (day == Day.FRIDAY) {
    // 處理星期五的邏輯
else if (day == Day.SATURDAY) {
    // 處理星期六的邏輯
else if (day == Day.SUNDAY) {
    // 處理星期日的邏輯
else {
    // 處理其他情況
}
  • • 最佳化後

// 使用 switch 處理列舉型別
switch (day) {
    case MONDAY:
        // 處理星期一的邏輯
        break;
    case TUESDAY:
        // 處理星期二的邏輯
        break;
    // ...
    default:
        // 處理其他情況
        break;
}

策略模式 + 簡單工廠模式

  • • 前面我們介紹一些常規、比較簡單的最佳化方法,但是在一些更加複雜的場景(比如多渠道對接、多方案實現等)我們可以結合一些場景的設計模式來實現讓我們的程式碼更加優雅和可維護性,比如策略模式 + 簡單工廠模式。

  • • 最佳化前

    public static void main(String[] args) {
        // 比如我們商場有多個通知渠道
        // 我們需要根據不同的條件使用不同的通知渠道
        if ("滿足條件A") {
            // 構建渠道A
            // 通知
        } else if ("滿足條件B") {
            // 構建渠道B
            // 通知
        } else {
            // 構建渠道C
            // 通知
        }
    }
// 上面的程式碼不僅維護起來麻煩同時可讀性也比較差,我們可以使用策略模式 + 簡單工廠模式
  • • 最佳化後

import java.util.HashMap;
import java.util.Map;

// 定義通知渠道介面
interface NotificationChannel {
    void notifyUser(String message);
}

// 實現具體的通知渠道A
class ChannelA implements NotificationChannel {
    @Override
    public void notifyUser(String message) {
        System.out.println("透過渠道A傳送通知:" + message);
    }
}

// 實現具體的通知渠道B
class ChannelB implements NotificationChannel {
    @Override
    public void notifyUser(String message) {
        System.out.println("透過渠道B傳送通知:" + message);
    }
}

// 實現具體的通知渠道C
class ChannelC implements NotificationChannel {
    @Override
    public void notifyUser(String message) {
        System.out.println("透過渠道C傳送通知:" + message);
    }
}

// 通知渠道工廠
class NotificationChannelFactory {
    private static final Map<String, Class<? extends NotificationChannel>> channelMap = new HashMap<>();

    static {
        channelMap.put("A", ChannelA.class);
        channelMap.put("B", ChannelB.class);
        channelMap.put("C", ChannelC.class);
    }

    public static NotificationChannel createChannel(String channelType) {
        try {
            Class<? extends NotificationChannel> channelClass = channelMap.get(channelType);
            if (channelClass == null) {
                throw new IllegalArgumentException("不支援的通知渠道型別");
            }
            return channelClass.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("無法建立通知渠道", e);
        }
    }
}

// 客戶端程式碼
public class NotificationClient {
    public static void main(String[] args) {
        // 根據條件選擇通知渠道型別
        String channelType = "A";
        // 使用簡單工廠建立通知渠道
        NotificationChannel channel = NotificationChannelFactory.createChannel(channelType);

        // 執行通知
        channel.notifyUser("這是一條通知訊息");
    }
}
  • • 有時候我們還可以藉助 Spring IOC 能力的自動實現策略類的匯入,然後使用 getBean() 方法獲取對應的策略類例項,可以根據我們的實際情況靈活選擇。

如何最佳化開頭的程式碼

  • • 好了現在回到開頭,如果是你會進行怎麼最佳化,下面是我交出的答卷,大家也可以在評論區發表自己的看法,歡迎一起交流:

   public static void main(String[] args) {
        // do something
        if (isMeetCondition()) {
            // 查詢配置
            // 此處查詢配置的值需要在具體的任務中使用,所有並沒抽離
            if ("配置是否開啟") {
                // do something
            }
        }
        // do something
    }

    /**
     * 判斷是否滿足執行條件
     */

    private static boolean isMeetCondition() {
        if (!"滿足條件A") {
            return false;
        }
        // 查詢許可權
        return "是否具備許可權A" && "是否具備許可權B";
    }

來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70024922/viewspace-3004148/,如需轉載,請註明出處,否則將追究法律責任。

相關文章