一次專案程式碼重構-使用spring容器幹掉條件判斷

何白白發表於2019-06-27

一次專案程式碼重構-使用spring容器幹掉條件判斷

這是在一次公司專案中進行重構時,一些複雜業務時想到的一個去掉一些if else的辦法。能夠使程式碼邏輯更加清晰,減少一些業務上的耦合。

業務說明

我所在的是一個做保險的專案組,這次重構是針對其中的保費計算和核保的業務。

專案重構之前,在保費計算的介面中,有大量的條件判斷語句來判斷這次進行保費計算的產品是哪一個,然後呼叫該產品的保費計算方法。程式碼大致看起來就是這個樣子:

//產品編號
String product = "123123";
if (product.equals("11111")) {
} else if (product.equals("11111")) {
    //使用產品編號是11111的service類進行保費計算
    // 
} else if (product.equals("22222")) {
    //使用產品編號是22222的service類進行保費計算
    // 
} else {
    //執行其他的保費計算
}

這些通過一個編號進行判斷並執行特定程式碼的方法在專案中到處都是,一旦新增了新的產品,或者產品在一些特定銷售渠道中有特定的一些操作(比如有一些折扣啊什麼的)就要在程式碼的各種關鍵地方新增一堆 if eles,大大影響了程式碼的可讀性和可維護性。常常是修改了一處程式碼,影響到很多別的地方,造成一些看起來很奇怪的bug。

所以我在接手這個專案的時候想到了重構。希望在重構後能夠使各個產品之間的程式碼沒有關聯,業務分明,相互不影響。

開始重構

分析業務,抽象出介面

重構程式碼之前要先分析一下業務,因為我的這個專案是做保費計算的,雖然有一大堆的判斷產品編號的程式碼,但是最終它們做的都是同一件事情。這裡可以把保費計算抽象成為一個介面,在介面中定義執行保費計算所需要的共同的方法。大致上是這個樣子:

/**
 * 保費計算介面
 *
 * @author hjx
 */
public interface PremiumCalculate {

    //保費計算
    Result calculate(Param param);

    //其他的保費計算中需要的方法
    。。。
}

這一步主要是梳理一下專案中的業務,並把業務中相同的步驟抽象出來。

根據不同的產品實現不同產品的保費計算

這一步就是實現上面的介面了,每個產品實現一遍介面。這裡會存在一個問題,就是有很多產品在進行保費計算的時候,只有某幾個步驟不一樣。如果每個實現上都寫一遍,會造成大量的重複程式碼。

我們可以建一個抽象類來實現這個介面,將大部分共有的實現程式碼寫在抽象類中。就像這樣:

/**
 * 抽象的保費計算
 */
public abstract class AbstractPremiumCalculate implements PremiumCalculate {
    /**
     * 實現共有的一些程式碼
     * @param premiumInfo
     * @return
     */
    @Override
    public Result calculate(Param param) {
        //將共有的實現寫在這裡
    }
}

這樣在新新增一個實現類的時候只要繼承這個抽象類,重寫其中的某些方法就可以了。這時我們可能有很多實現類,比如:

  • PremiumCalculateP001Impl.java

  • PremiumCalculateP002Impl.java

  • PremiumCalculateP003Impl.java

  • PremiumCalculateP004Impl.java

    這時就可以在執行保費計算的時候根據不同的產品呼叫不同的實現。每個實現類只寫一個產品的業務就行,類之間相互不影響。在新新增產品的時候也是隻需要新增一個新的實現類就好了。

但是這樣還有問題

但是這樣還是有問題的,因為還是要在業務程式碼中寫一堆的if else 來判斷這次到底需要哪一個實現類來執行保費計算,這時可以寫一個工廠類。根據傳入的產品編號或者別的什麼引數,返回特定的一個實現類,像是這樣:

/**
 * 保費計算介面工廠類
 */
@Service
public class PremiumCalculateFactory {

    @Autowired
    @Qualifier("premiumCalculateP0001")
    private PremiumCalculate premiumCalculateP0001;

    @Autowired
    @Qualifier("premiumCalculateP0002")
    private PremiumCalculate premiumCalculateP0002;

    @Autowired
    @Qualifier("premiumCalculateP0003")
    private PremiumCalculate premiumCalculateP0003;
    
    @Autowired
    @Qualifier("premiumCalculateP0004")
    private PremiumCalculate premiumCalculateP0004;
    
    。。。。其他的保費計算實現物件

    public PremiumCalculate getPremiumCalculate(String productCode) {

        if (productCode.equals("P0001")) {
            return premiumCalculateP0001;
        } else if () {
           //其他的條件下,返回其他的物件
        }
    }

}

新增了工廠類之後,我們在獲取保費計算物件的時候只需要呼叫getPremiumCalculate()方法就可以了,具體返回哪一個實現物件,就交給工廠類來處理。可以精簡呼叫保費計算時的程式碼。

還是免不了寫if else,改造PremiumCalculateFactory

在提供了工廠類之後,還是免不了寫很多的條件判斷,只不過是把所有的條件判斷寫在了一起。這時隨著產品數量的增多,if else 也會不停地增多,維護起來依然費勁。

這裡spring容器就可以排上用場了。spring中有一個BeanFactory物件,也是一個工廠,我們可以用它來改造PremiumCalculateFactory。

首先我們在編寫各個產品對應的保費計算實現類的時候都會將它註冊進spring容器中,成為一個bean。我們需要給這個bean指定一個名稱比如:

//在這裡指定bean的名稱
@Service("PremiumCalculate:"+產品編號)
public class PremiumCalculateP0001Impl implements PremiumCalculate {
    。。。
}

然後修改PremiumCalculateFactory

/**
 * 保費計算介面工廠類
 */
@Service
public class PremiumCalculateFactory {
    @Autowired
    private BeanFactory beanFactory;
    
    public PremiumCalculate getPremiumCalculate(String productCode) {
        Object bean = beanFactory.getBean("PremiumCalculate:" + productCode);
        if (bean instanceof PremiumCalculate) {
            return (PremiumCalculate) bean;
        }
        throw new UnsupportedOperationException("不支援的編號:" + productCode);
    }
}

這樣,條件判斷的步驟就可以省略了。

結果

在保費計算和核保專案經過這樣重構後,每個產品的業務程式碼相互不關聯,維護和新增產品時也能減少工作量,還是比較成功的。

不足

這樣寫會有一個比較大的問題,就是在產品數量增多的時候,java檔案數量也會隨之變多。但是目前的業務中產品數量還可以忍受。由於產品配置功能的出現,大部分產品都可以通過資料庫配置出來。這裡只是寫配不出來的一部分,所以這種模式還是可行的。

沒了

https://www.cnblogs.com/hebaibai/p/11095590.html

相關文章