阿里java開發三年程序員:不吹牛X,我輕鬆幹掉了if-else

前言

雖然 if else 是必須的,但濫用 if else 會對代碼的可讀性、可維護性造成很大傷害,進而危害到整個軟件系統。

現在軟件開發領域出現了很多新技術、新概念,但 if...else 這種基本的程序形式並沒有發生太大變化。

使用好 if else 不僅對於現在,而且對於將來,都是十分有意義的。今天我們就來看看如何“幹掉”代碼中的 if else,還代碼以清爽。

我們在web開發中,經常使用數據庫表中的字段作爲“標記”來表示多個“狀態”,比如:

我們就以某寶的在線購物流程爲例進行分析。在訂單表中,使用zt字段來表示訂單的狀態,常見的狀態就有:

阿里java開發三年程序員:不吹牛X,我輕鬆幹掉了if-else

 

當我們想按條件查詢各個類型的訂單的時候,只需要一個接口,在前端傳入相應的狀態碼就可以了。在dao層大概也就是通過如下的語句進行查詢:

select * from orders where zt = #{zt}

如何纔能有很高的擴展性?

假設有這麼幾個“不成需求的需求”:

  1. 我想讓待收貨的訂單按照訂單發貨時間或者預計送達時間排序,其他的暫且按照訂單創建時間排序吧
  2. 想將“待收貨”的狀態區分開,分爲“用戶未收到貨”和“用戶收到貨但是未點擊確認收貨按鈕”兩種狀態

常規方式如何解決?

  • ​ 需求一(不同的狀態處理方式不同):這個很容易的,在sevice層添加一個判斷就可以,其他的代碼不用改,代碼如下:
// 2 表示待收貨
if(zt == 2){
  //按照需求,按照訂單發貨時間或者預計送達時間排序
}else{
  //其他狀態的訂單,全部按照訂單創建時間排序
}

​ 上邊這個代碼的修改量已經很小了,但是如果我要把各種不同的狀態訂單全部按照不同的排序方式排序呢?你可能會寫如下代碼

if(zt==0){
  // 待付款的訂單處理代碼...
}else if(zt==1){
  
}else if(zt==2){
  
}else if(zt==3){
  
}else if(zt==4){
  
}

上邊代碼太low了,有些小夥伴可能會使用switch進行優化(這裏就不寫代碼了,因爲和上邊並沒有任何區別)。

  • 需求二(添加一個新的狀態表示):這也很easy啊,直接在上邊的if-else或者switch代碼中添加新的狀態判斷不就好了。

思考如何幹掉if-else?

上邊的方式可以完成我們的需求,但是有以下幾點不足:

  1. 面對“各種各樣奇怪的需求”,我們要頻繁地修改上邊的代碼,時間久了,豈不成了渣渣。甚至我們自己都不願意再去看這些代碼了;
  2. 如果新增加一個狀態表示,也就是給zt字段新的狀態含義表示,我們又要添加if-else,這太複雜了。

使用策略模式來解決if-else的問題

是的,就是使用策略模式來解決進行太多的狀態判斷代碼就是一個好辦法。比如,就上邊每一個if-else中的代碼抽成一個類或者方法進行處理。

主要的代碼我就不寫了,因爲下邊纔是我們的主菜,這裏說的這種方式只能解決if-else裏邊的代碼複雜問題,將代碼進行一定程度上的解耦。但並沒有實質地解決if-else的問題,而且這也是網上大多數的解決辦法。

如果對策略模式不太瞭解的小夥伴,可以看下這篇文章,不看也沒關係,在下邊你會看到怎麼用的。策略模式的學習之道

嘗試使用Spring來配合策略模式

程序設計的一大原則“對擴展開放,對修改關閉”,定義一個接口類,用來查詢不同狀態的訂單列表。如下:

public interface OrderService {

    /**
     * 查詢對應狀態的訂單列表
     * @param zt
     * @return
     */
    List<Order> getOrderList(String zt);
}

然後根據不同的訂單狀態創建不同的實現類,比如,“待付款”的訂單查詢類如下:

雖然以下的命名方式屬於錯誤示範,但是卻能很好地理解

@Service("orderServiceDfk") // 這個命名確實很不友好,但是我相信你能理解哈
public class PendingParymentOrderSeviceImpl implements OrderService {
    /**
     * 查詢待付款的訂單列表
     *
     * @param zt
     * @return
     */
    @Override
    public List<Order> getOrderList(String zt) {
      //這裏要利用dao層從數據庫中查詢出來相應的訂單列表
      return null;
    }

看了這兩個類的代碼,我相信小夥伴們應該能理解了要怎麼做了,就是根據前端傳來不同的zt值,後臺使用不同的類來處理,但是我們可以通過Spring來完全去掉if-else。

我們的controller層代碼如下:

@RestController
public class OrderController {
  private String orderServiceBeanNamePrefix = "orderService";

  @RequestMapping("getOrderList/{zt}")
  public List<Order> getOrderList(@PathVariable("zt") String zt) {

    //獲取對應的處理狀態的bean來處理
    //就通過這樣一句代碼,完全解決了if-else的判斷邏輯
    OrderService orderService = (OrderService) SpingContext.getBean(orderServiceBeanNamePrefix + zt);
    List<Order> orderList = orderService.getOrderList();

    return orderList;
  }
}

上邊用了一個工具類,就是從Spring 容器中獲取相應的bean,代碼如下:

/**
 *
 * 原理很簡單,我們寫的類實現這個接口,具體可以查閱Spring生命週期相關內容
 * Spring會自動調用其中的setApplicationContext方法,傳入Spring容器上下文
 * 我們就在這裏把Spring上下文保存下來
 *
 * @date 2020/5/18
 * @auther Lyn4ever
 */
@Component
public class SpingContext implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    /**
     * 根據name從Spring容器中獲取bean
     * @param name
     * @return
     */
    public static Object getBean(String name){
        return applicationContext.getBean(name);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("我保存了Spring上下文");
        applicationContext = applicationContext;
    }
}

總結:

解決if-else的思路就是使用策略模式,針對不同“狀態”的訂單,使用不同的類來處理邏輯,這樣就可以很好地進行了“解耦”操作。但是,如果新增一個“狀態表示 ”,我們就要在主邏輯處添加if-else進行判斷要用哪個類來處理。

而解決這個“判斷 ”的中使用的if-else就有很多方法:抽象工廠也是一個不錯的方法。而我們使用Spring的控制反轉同樣也可以很好地解決這個問題。這麼做的好處如下:

  1. “真正的”解決了與我們業務無關的if-else;
  2. 不用前後端再進行狀態的表示“約定”,之前用0表示“待付款”,1表示 “待發貨”這樣的操作,如果記錯,那一定會有大問題。現在,使用特定的字符串來表示,也就是說,前端直接傳入想要解決這個方案對應的bean,從而少去了“複雜且易出錯的約定”環節。

正如前言所說,if else 是代碼中的重要組成部分,但是過度、不必要地使用 if else,會對代碼的可讀性、可擴展性造成負面影響,進而影響到整個軟件系統。

“幹掉”if else 的能力高低反映的是程序員對軟件重構、設計模式、面向對象設計、架構模式、數據結構等多方面技術的綜合運用能力,反映的是程序員的內功。

要合理使用 if else,不能沒有設計,也不能過度設計。這些對技術的綜合、合理的運用都需要程序員在工作中不斷的摸索總結。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章