如何優雅的消除系統重複程式碼

語言: CN / TW / HK

引言

很多同學在工作一段時間之後可能都有這樣的困境,大家覺得自己總是在寫業務程式碼,技術上感覺好像沒有多大的長進,不知不覺就成為了CURD Boy或者Girl,自己想要去改變但是又不知道該從何處進行入手。有的同學會去學習如何做架構、有的同學可能會去學習各種新技術還有的同學甚至轉產品經理來試圖解除困境。但是我覺得找到跨出這種困境的途徑反而還是要從我們每天寫的程式碼入手。即便當前每天做著CRUD的事情,但是我們自己不能把自己定義為只會CURD的工具人。那麼我們到底如何從程式碼層面入手改變困境呢?我們可以回過頭看看自己以前寫的程式碼,或者是當前正在實現的各種各樣的需求,反問自己以下5個問題。

  • 有沒有使用設計模式優化程式碼結構?
  • 有沒有利用一些高階特性來簡化程式碼實現?
  • 有沒有藉助框架的能力來擴充套件應用能力?
  • 自己設計的業務模型夠不夠抽象?
  • 程式碼擴充套件性強不強,需求如果有變化模組程式碼能不能做到最小化修改?

通過這樣的反問和思考,我們可以不斷自我審視自己寫的程式碼。通過在程式碼上的深耕細作,我們所負責的模組的質量就會比別人更高,出現Bug的概率就會更低,穩定性就會更高,那麼未來負責更多業務模組的機會也就會更多,只有這樣我們才能真正跨出困境,實現突破。因此本文主要從優化日常工作中經常遇到的重複程式碼入手,和大家探討下如何通過一些技巧來消除平臺中的重複程式碼,以消除系統中的重複程式碼為切入點,提升系統穩定性。

為什麼要消除重複程式碼

在程式猿的日常工作中,不僅要跟隨業務側的發展不斷開發新的需求,同時也需要維護老的已有平臺。無論是開發新需求還是維護老系統,我們都會遇到同樣一個問題,系統中總是充斥著很多重複的程式碼。可能是由於工期很趕沒時間優化,也有可能是歷史原因欠下的技術債。無論是什麼原因,系統中大量的重複程式碼非常影響平臺整體的可維護性。大神們的諄諄教導Don’t Repeat Yourself 言猶在耳。那麼平臺中的重複程式碼會帶來怎樣的穩定性風險呢?

系統維護成本高

如果專案中出現大量的重複程式碼,說明系統中這部分業務邏輯並沒有進行很好的抽象,因此會導致後期的程式碼維護面臨很多問題。無論是修改原有邏輯還是新增業務邏輯可能需要在不同的檔案中進行修改,專案維護成本相當高。另外後期維護的同學看到同樣的邏輯寫了多遍,不明白這到底是程式碼的壞味道還是有什麼特殊的業務考慮,這也在無形中增加了後期維護者的程式碼邏輯理解難度。

程式Bug概率高

大家都知道重複程式碼意味著業務邏輯相同或者相似,假如這些相同或者相似的程式碼出現了Bug,在修復的過程中就需要修改很多地方,導致一次上線變更的內容比較多,存在一定的風險,畢竟線上問題70%-80%都是由於新的變更引起的。另外如果重複的地方比較多,很有可能出現漏改的情況。因此重複的程式碼實際就是隱藏在工程中的老炸彈,可能一直相安無事,也可能不知道什麼時候就會Bom一聲給你驚喜,因此我們必須要進行重複程式碼消除。

如何優雅的消除重複程式碼

在消除重複程式碼之前,我們首先需要確定到底什麼是重複程式碼,或者說重複程式碼的特徵到底是什麼。有的同學可能會說,這還不簡單嘛,重複程式碼不就是那些一模一樣的但是散落在工程不同地方的程式碼嘛。當然這句話也沒錯,但是不夠全面,重複程式碼不僅僅指那些不同檔案中的完全相同的程式碼,還有一些程式碼業務流程相似但是並不是完全相同,這類程式碼我們也把它稱之為重複程式碼。重複程式碼的幾個特性:

  • 程式碼結構完全相同

比如工程中好幾個地方都有讀取配置檔案的邏輯,程式碼都是相同的,那麼我們可以把不同地方讀取配置檔案的邏輯放到一個工具類中,這樣今後再有讀取配置檔案的需要的時候可以直接呼叫工具類中方法即可,不需要再重複寫相同的程式碼,這也是我們日常工作中最常見的使用方式。

  • 程式碼邏輯結構相似

在專案中經常遇到雖然程式碼並不是完全相同,但是邏輯結構卻非常相似。比如電商平臺在進行營銷活動的時候,常常通過邀請的方式來進行使用者紅包領取的活動,但是對於新老使用者的紅包贈予規則是不同的,同時也會根據邀請使用者的數量的不同給予不同的紅包優惠。但是無論新老使用者都會經歷根據使用者型別獲取紅包計算規則,根據規則計算減免的紅包,最後付款的時候減去紅包數額這樣一個業務邏輯。雖然表面看上去程式碼並不相同,但是實際上邏輯基本是一樣的,因此也屬於重複程式碼。

下面就和大家分享幾種比較實用的消除重複的程式碼的技巧,考慮到安全性,程式碼都進行了脫敏以及簡化處理。

統一引數校驗

當我們進行專案開發的時候,會編寫一些類的實現方法,不可避免的會進行一些引數校驗或者業務規則校驗,因此會在實現方法中寫一些判斷引數是否有效或者返回結果是否有效的的的程式碼。

public OrderDTO queryOrderById(String id) {
    if(StringUtils.isEmpty(id)) {
        return null;
    }
    
    OrderDTO order = orderBizService.queryOrder(id);
    if(Objects.isNull(Order)) {
        return null;
    }
    ...
}

public List<UserDTO> queryUsersByType(List<String> type) {
    if(StringUtils.isEmpty(id)) {
        return null;
    }
    
    ...
}

這種引數校驗的方式,很多人會喜歡使用@Valid這種註解來進行引數有效性的判斷,但是我覺得還是不夠方便,它只能進行一些引數的校驗,並不能進行業務結果的有效性判斷。那麼對於這種校驗類的程式碼如何才能消除重複if...else...判斷程式碼呢?因此我一般會統一定義一個Assert斷言來進行引數或者業務結果的校驗,當然也可以使用Spring框架提供的Assert抽象類來進行判斷,但是它丟擲的異常是IllegalArgumentException,我習慣丟擲自己定義的全域性統一的異常資訊,這樣可以通過全域性的異常處理類來進行統一處理。因此我們首先定義一個業務斷言類,主要針對biz層出現的引數以及業務結果進行斷言,這樣可以避免重複寫if...else...判斷程式碼。

public class Assert {

    public static void notEmpty(String param) {
        if(StringUtils.isEmpty(param)) {
            throw new BizException(ErrorCodes.PARAMETER_IS_NULL, "param is empty or null");
        }
    }

    public static void notNull(Object o) {
        if (Objects.isNull(o)) {
            throw new BizException(ErrorCodes.PARAMETER_IS_NULL, "object is null");
        }
    }

    public static void notEmpty(Collection collection) {
        if(CollectionUtils.isEmpty(collection)) {
            throw new BizException(ErrorCodes.PARAMETER_IS_NULL, "collection is empty or null");
        }
        
    }
    
}

我們看下優化後的程式碼是不是看上去清爽許多。

public OrderDTO queryOrderById(String id) {
    Assert.notEmpty(id);
    OrderDTO order = orderBizService.queryOrder(id);
    Assert.notNull(order);
    ...
}

public List<UserDTO> queryUsersByType(List<String> type) {
    Assert.notEmpty(type);
    
    
    ...
}

統一異常處理

以下這類Controller程式碼在專案中是不是很常見?大家可以翻翻自己的專案工程程式碼,可能很多工程中Cotroller層都充斥著這樣的try{}catch{}邏輯處理,相當於每個介面實現都要進行異常處理,看起來非常冗餘寫起來也麻煩。實際上我們可以通過定義統一的全域性異常處理器來進行優化,避免重複的進行異常捕獲。

@GetMapping("list")
public ResponseResult<OrderVO> getOrderList(@RequestParam("id")String userId) {
    try {
        OrderVO orderVo = orderBizService.queryOrder(userId);    
        return ResponseResultBuilder.buildSuccessResponese(orderDTO);
    } catch (BizException be) {
        // 捕捉業務異常
        return ResponseResultBuilder.buildErrorResponse(be.getCode, be.getMessage());
    } catch (Exception e) {
        // 捕捉兜底異常
        return ResponseResultBuilder.buildErrorResponse(e.getMessage());
    }
}

那麼我們應該怎麼優化這些重複的異常捕捉處理程式碼呢?首先我們需要定義一個統一的異常處理器,通過它來對Controller介面的異常進行統一的異常處理,包括異常捕獲以及異常資訊提示等等。這樣就不用在每個實現介面中編寫try{}catch{}異常處理邏輯了。示意程式碼只是簡單的說明實現方法,在專案中進行落地的時候,大家可以定義處理更多的異常型別。

@ControllerAdvice
@ResponseBody
public class UnifiedException {

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(BizException.class)
    @ResponseBody
    public ResponseResult handlerBizException(BizException bizexception) {
            return ResponseResultBuilder.buildErrorResponseResult(bizexception.getCode(), bizexception.getMessage());
    }


    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResponseResult handlerException(Exception ex) {
        return ResponseResultBuilder.buildErrorResponseResult(ex.getMessage());
    }   
}

優化後的Controller如下所示,大量的try...catch...不見了,程式碼結構變得更加清晰直接。

@GetMapping("list")
public ResponseResult<OrderVO> getOrderList(@RequestParam("id")String userId) {
        List<OrderVO> orderVo = orderBizService.queryOrder(userId);    
        return ResponseResultBuilder.buildSuccessResponese(orderVo);
}

優雅的屬性拷貝

在實際的專案開發中我們所開發的微服務都是分層的有的是MVC三層,有的按照DDD領域分層是四層。無論是三層還是四層都會涉及不同層級的之間的呼叫,而每個層級都有自己的資料物件模型,比如biz層是dto,domain層是model,repo層是po。因此必然會涉及到資料模型物件之間的相關轉換。在一些場景下模型之間的欄位很多都是一樣的,有的甚至是完全一模一樣。比如將DTO轉化為業務模型Model,實際上他們之間很多的欄位都是一樣的,所以經常會出現以下的這種程式碼,會出現大量的屬性賦值 的操作來達到模型轉換的需求。實際上我們可以通過一些工具包或者工具類進行屬性的拷貝,避免出現大量的重複賦值程式碼。

public class TaskConverter {

    public static TaskDTO taskModel2DTO(TaskModel taskModel) {
        TaskDTO taskDTO = new TaskDTO();
        taskDTO.setId(taskModel.getId());
        taskDTO.setName(taskModel.getName());
        taskDTO.setType(taskModel.getType());
        taskDTO.setContent(taskModel.getContent());
        taskDTO.setStartTime(taskModel.getStartTime());
        taskDTO.setEndTime(taskModel.getEndTime());
        return taskDTO;

    }
}

使用BeanUtils的進行屬性賦值,很明顯不再有那又長又沒有感情的一條又一條的屬性賦值語句了,整個任務資料模型物件的轉換程式碼看上去立馬舒服很多。

public class TaskConverter {
    
    public static TaskDTO taskModel2DTO(TaskModel taskModel) {
        TaskDTO taskDTO = new TaskDTO();
        BeanUtils.copyProperties(taskModel, taskDTO);
        return taskDTO;
    }

}

當然很多人會說,BeanUtils會存在深拷貝的問題。但是在一些淺拷貝的場景下使用起來還是比較方便的。另外還有Mapstruct工具,大家也可以試用一下。

核心能力抽象

假設有這樣的業務場景,系統中需要根據不同的使用者型別計算商品結算金額,大致的計算邏輯有三個步驟,分別是計算使用者商品總價格,計算不同使用者對應的優惠金額,最後計算出使用者的結算金額。我們先來看下原有系統中的實現方式。

普通使用者結算邏輯:

public Class NormalUserSettlement {
    
     //省略程式碼
    ...

    public Bigdecimal calculate(String userId) {
        //計算商品總價格
        List<Goods> goods = shoppingService.queryGoodsById(userId);
        
        Bigdecimal total = goods.stream().map(item -> item.getPrice().multiply(BigDecimal.valueOf(item.getAmount()))).reduce(BigDecimal.ZERO, BigDecimal::add);
          
        //計算優惠
        Bigdecimal discount = total.multiply(new Bigdecimal(0.1));          
            
        //計算應付金額
        Bigdecimal payPrice = total - dicount;
        return payPrice;
    }
     //省略程式碼
    ...
}

VIP使用者結算邏輯:

public Class VIPUserSettlement {
    
    //省略程式碼
    ...

    
   public Bigdecimal calculate(String userId) {
        //計算商品總價格
        List<Goods> goods = shoppingService.queryGoodsById(userId);
        
        Bigdecimal total = goods.stream().map(item -> item.getPrice().multiply(BigDecimal.valueOf(item.getAmount()))).reduce(BigDecimal.ZERO, BigDecimal::add);
          
        //計算優惠
        Bigdecimal discount = total.multiply(new Bigdecimal(0.2));          
            
        //計算應付金額
        Bigdecimal payPrice = total - dicount;   
        return payPrice;
    }
     //省略程式碼
    ...
}

黑卡使用者結算邏輯:

public Class VIPUserSettlement {
    
    //省略程式碼
    ...

    
   public Bigdecimal calculate(String userId) {
        //計算商品總價格
        List<Goods> goods = shoppingService.queryGoodsById(userId);
        
        Bigdecimal total = goods.stream().map(item -> item.getPrice().multiply(BigDecimal.valueOf(item.getAmount()))).reduce(BigDecimal.ZERO, BigDecimal::add);
          
        //計算優惠
        Bigdecimal discount = total.multiply(new Bigdecimal(0.2));          
            
        //計算應付金額
        Bigdecimal payPrice = total - dicount;       
        return payPrice;   
    }    
     //省略程式碼
    ...
}

在這樣的場景下,我們可以發現,在三個類中計算商品總額以及計算最後的應付金額邏輯都是一樣的,唯一不同的是每個使用者型別對應的優惠金額是不同的。因此我們可以把邏輯相同的部分抽象到AbstractSettleMent中,然後定義計算優惠金額的抽象方法由各個不同的用型別子類去實現。這樣各個子類只要關心自己的優惠實現就可以了,重複的程式碼都被抽象複用大大減少重複程式碼的使用。

public Class AbstractSettlement {
    
   //省略程式碼
    ...
        
    public abstact   Bigdecimal calculateDiscount();

    
   public Bigdecimal calculate(String userId) {
        //計算商品總價格
        List<Goods> goods = shoppingService.queryGoodsById(userId);
        
        Bigdecimal total = goods.stream().map(item -> item.getPrice().multiply(BigDecimal.valueOf(item.getAmount()))).reduce(BigDecimal.ZERO, BigDecimal::add);
          
        //計算優惠
        Bigdecimal discount = calculateDiscount();          
            
        //計算應付金額
        Bigdecimal payPrice = total - dicount;     
        return payPrice; 
    
    }
    
     //省略程式碼
    ...
}

自定義註解和AOP

用過Spring框架的同學都知道,AOP是Spring框架核心特性之一,它不僅是一種程式設計思想更是實際專案中可以落地的技術實現技巧。通過自定義註解和AOP的組合使用,可以實現一些通用能力的抽象。比如很多介面都需要進行鑑權、日誌記錄或者執行時間統計等操作,但是如果在每個介面中都編寫鑑權或者日誌記錄的程式碼那就很容易產生很多重複程式碼,在專案後期不好維護。針對這種場景 我們可以使用AOP同時結合自定義註解實現介面的切面程式設計,在需要進行通用邏輯處理的介面或者類中增加對應的註解即可。

假設有這樣的業務場景,需要計算指定某些介面的耗時情況,一般的做法是在每個介面中都加上計算介面耗時的邏輯,這樣各個介面中就會有這樣重複計算耗時的邏輯,重複程式碼就這樣產生了。那麼通過自定義註解和AOP的方式可以輕鬆地解決程式碼重複的問題。首先定義一個註解,用於需要統計介面耗時的介面方法上。

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TimeCost {

}

定義切面實現類:

@Aspect 
@Component
public class CostTimeAspect {

    @Pointcut(value = "@annotation(com.mufeng.eshop.anotation.CostTime)") 
    public void costTime(){ }
    
    @Around("runTime()") 
    public Object costTimeAround(ProceedingJoinPoint joinPoint) {
        Object obj = null;
  try {
   long beginTime = System.currentTimeMillis();
   obj = joinPoint.proceed();
   //獲取方法名稱
   String method = joinPoint.getSignature().getName();
   //獲取類名稱
   String class = joinPoint.getSignature().getDeclaringTypeName();
                        //計算耗時
                        long cost = System.currentTimeMillis() - beginTime;
   log.info("類:[{}],方法:[{}] 介面耗時:[{}]", class, method, cost + "毫秒");
  } catch (Throwable throwable) {
   throwable.printStackTrace();
  }
  return obj;
    }
}

優化前的程式碼:

@GetMapping("/list")
public ResponseResult<List<OrderVO>> getOrderList(@RequestParam("id")String userId) {
        long beginTime = System.currentTimeMillis();
        List<OrderVO> orderVo = orderBizService.queryOrder(userId);  
        log.info("getOrderList耗時:" + System.currentTimeMillis() - beginTime + "毫秒");
        return ResponseResultBuilder.buildSuccessResponese(orderVo);
}


@GetMapping("/item")
public ResponseResult<OrderVO> getOrderById(@RequestParam("id")String orderId) {
        long beginTime = System.currentTimeMillis();
        OrderVO orderVo = orderBizService.queryOrderById(orderId);
        log.info("getOrderById耗時:" + System.currentTimeMillis() - beginTime + "毫秒");
        return ResponseResultBuilder.buildSuccessResponese(orderVo);
}

優化後的程式碼:

@GetMapping("/list")
@TimeCost
public ResponseResult<List<OrderVO>> getOrderList(@RequestParam("id")String userId) {
        List<OrderVO> orderVo = orderBizService.queryOrder(userId);    
        return ResponseResultBuilder.buildSuccessResponese(orderVo);
}


@GetMapping("/item")
@TimeCost
public ResponseResult<OrderVO> getOrderList(@RequestParam("id")String orderId) {
        OrderVO orderVo = orderBizService.queryOrderById(orderId);    
        return ResponseResultBuilder.buildSuccessResponese(orderVo);
}

引入規則引擎

大家在做業務開發的時候,可能會遇到這樣的場景,業務中充斥著各種各樣的規則判斷,同時這些業務規則還可能經常發生變化。即便是我們用了策略模式等設計模式來優化程式碼結構,但是還是不能避免程式碼中出現大量的if...else...判斷程式碼,一旦增加或者修改規則都需要在原來的業務規則程式碼中進行修改,維護起來非常不方便。

假設設有這樣的業務,銷售人員的獎勵根據實際的利潤進行計算,不同的利潤計算獎勵的規則並不相同。使用規則引擎之前,可能會有這樣的程式碼結構,需要根據實際利潤所處的區間來計算最終的獎勵金額,不同區間範圍對應的返點規則是不一樣的,因此會有很多的if...else...判斷。另外規則有可能隨著業務的發展還會經常變化,因此後期可能面臨不斷修改這部分的計算獎勵的程式碼的情況。

public double calculate(int profit) {
        if(profit < 1000) {
            return profit * 0.1;
        } else if(1000 < profit && profit< 2000) {
            return profit * 0.15;
        } else if(2000 < profit && profit < 3000) {
            return profit * 0.2;
        } 
        return  profit * 0.3;
    }

如果遇到這種業務場景,我們就可以考慮使用規則引擎。通過引入規則引擎,我們可以實現業務程式碼與業務規則相分離,將各種業務判斷規則從原有的平臺程式碼中抽離出來,以後規則的修改都在規則檔案中直接修改就可以了,避免程式碼本身的變更,從而大大提升程式碼的擴充套件性。這裡簡單介紹下常用的規則引擎Drools是如何實現規則擴充套件管理的。

使用Drools之後:

使用規則引擎優化之後,所有的規則也就是所有的if...else...都會放在規則檔案reward.drl中,因此程式碼中不會再有各種重複的if...else...程式碼,真正實現了業務規則與業務資料相分離。

// 獎勵規則
package reward.rule
import com.mufeng.eshop.biz.Reward
 
// rule1:如果利潤小於1000,則獎勵計算規則為profit*0.1
rule "reward_rule_1"
    when
        $reward: Reward(profit < 1000) 
    then
        $reward.setReward($reward.getProfit() * 0.1);
        System.out.println("匹配規則1,獎勵為利潤的1成");
end
 
// rule2:如果利潤大於1000小於2000,則獎勵計算規則為profit*0.15
rule "reward_rule_2"
    when
        $reward: Reward(profit >= 1000 && profit < 2000)
    then
        $reward.setReward($reward.getProfit() * 0.15);
        System.out.println("匹配規則2,獎勵為利潤的1.5成");
end
 
// rule3:如果利潤大於2000小於3000,則獎勵計算規則為profit*0.2
rule "reward_rule_3"
    when
        $order: Order(profit >= 2000 && profit < 3000)
    then
        $reward.setReward($reward.getProfit() * 0.2);
        System.out.println("匹配規則3,獎勵為利潤的2成");
end
 
//  rule4:如果利潤大於等於3000,則獎勵計算規則為profit*0.3
rule "reward_rule_4"
    when
         $order: Order(profit >= 3000)
    then
        $reward.setReward($reward.getProfit() * 0.3);
        System.out.println("匹配規則4,獎勵為利潤的3成");
end

在程式碼中只要將待判斷的資料插入到規則引擎的工作記憶體中,然後執行規則就可以獲取到最終的結果,是不是很方便的實現業務規則的解耦,在實際的Java程式碼中也不用看到各種if...else...判斷。

定義規則引擎實現:

public class DroolsEngine {

    private KieHelper kieHelper;

    public DroolsEngine() {
        this.kieHelper = new KieHelper();
    }

    public void  executeRule(String rule, Object unit, boolean clear) {
        kieHelper.addContent(rule, ResourceType.DRL);
        KieSession kieSession = kieHelper.getKieContainer().newKieSession();
        //插入判斷實體
        kieSession.insert(unit);
        //執行規則
        kieSession.fireAllRules();
        if (clear) {
            kieSession.dispose();
        }
    }
}
public class Profit {


    public double  calculateReward(Reward reward) {
       String rule = "classpath:rules/reward.drl";
       File rewardFile = new File(rule);
       String rewardDrl = FileUtils.readFile(rewardFile, "utf-8");
       DroolsEngine engine = new DroolsEngine();
       engine.executeRule(rewardDrl, reward, true);
       return  reward.getReward();    
       
    }
}

通過引入Drools規則引擎,程式碼中不再有各種規則判斷的重複的if...else...判斷語句,而且如果後期要修改獎勵規則,程式碼不用修改,直接更改規則即可,系統的擴充套件性以及可維護性進一步提升。

消除重複程式碼方法論

上文中給大家介紹了幾種消除重複程式碼的實戰小技巧,不知道大家有沒有發現雖然具體落地實操的手段各不相同,無論是提取公用邏輯作為工具類、使用AOP進行面向切面程式設計還是進行通用邏輯抽象,又或者是藉助規則引擎分離實現與規則。實際它們的核心思想本質上都是一致的,都是通過抽離或者抽象相似程式碼邏輯後進行統一處理。將這種核心思想放在微服務內部就是在系統中的消除重複業務邏輯,如果放在架構層面來看其實和中臺思想的本質也是相通的,將使用者、支付這種各個平臺都會用到的服務抽象為中颱,實際就是一種混亂到有序的軟體複雜度治理過程以及一種萬物歸一的思想。

那麼在日常的實際專案中我們應該怎麼落地實踐消除重複程式碼呢?這裡總結了通過上述文章對於重複程式碼的處理,我們來試圖來提煉消除重複程式碼的方法論。

Find:技術同學需要有一雙可以發現重複程式碼的眼睛,能夠將表面上的重複我程式碼以及隱藏的重複程式碼識別出來。重複程式碼不僅僅是表示長得一模一樣的程式碼,那些核心業務邏輯一樣實際也是一種重複程式碼。

Analysis:當我們找到了重複程式碼之後,就要考慮該如何進行優化了,如果只是工具型別的重複程式碼,那麼直接提取作為一個工具類就可以了,也不用考慮太多。但是如果是涉及業務流程可能需要進一步的進行抽象。

Action:根據不同的重複程式碼的型別,我們需要制定不通過的優化重複程式碼的方案。根據不同的方案實現通過引入規則引擎還是模板方法進行抽象。

總結

不知不覺又到凌晨12點了,每次在這種夜深人靜的時候寫文章,也是自己最享受的時光。白天工作很忙,晚上又是各種加班,每天能留給自己的時間真的是少之又少。在睡覺前的這一個小時左右的時間,能夠將自己的總結和思考沉澱下來其實一件非常值得開心的事情,如果可以給看到文章的同學一點點啟發,那更是善莫大焉。今天和大家主要分享了幾種專案中消除重複程式碼的實踐方案,同時沉澱瞭如何優雅消除程式碼重複的方法論,希望通過這樣的沉澱以及總結可以在大家遇到同樣的問題的時候可以有所幫助,通過實際的優化程式碼落地來提升平臺的可維護性。大家有沒有在專案中實戰過的其他消滅重複程式碼的實踐案例呢?