一次代碼優化的實踐(模板方法+策略+工廠方法模式)


前言

好久沒分享工作總結啦,今天來一份代碼優化總結。用模板方法+策略+工廠方法模式優化了代碼,耐心點看完,應該對大家有幫助的~

本文已經收錄到github

https://github.com/whx123/JavaHome

公眾號:撿田螺的小男孩

優化代碼前

先來了解一下類似的業務場景,簡言之,就是:多個商戶接入我們系統,都是走一個類似的流程通過http請求出去的。

優化前,每個公司對應一個句柄服務,偽代碼如下:

// 商戶A處理句柄
CompanyAHandler implements RequestHandler {
   Resp hander(req){
   //查詢商戶信息
   queryMerchantInfo();
   //加簽
   signature();
   // http請求(走代理)
   httpRequestbyProxy()
   // 驗簽
   verify();
   }
}
// 商戶B處理句柄
CompanyBHandler implements RequestHandler {
   Resp hander(Rreq){
   //查詢商戶信息
   queryMerchantInfo();
   //加簽
   signature();
   // http請求(不走代理)
   httpRequestbyDirect();
   // 驗簽
   verify(); 
   }
}
// 商戶C處理句柄
CompanyBHandler implements RequestHandler {
   Resp hander(Rreq){
   //查詢商戶信息
   queryMerchantInfo();
   // webservice 方式調用
   requestByWebservice();
   }
}

優化代碼思路

我的優化代碼思路,是有重復代碼,先把它抽出來,或者公用變量,或者公用方法,伸着公用類。所以呢,查詢商戶信息呀,加簽呀,http請求呀先全部各抽成一個公用方法。你細心點會發現,連每個Handler處理過程都很類似的,大概都是查詢商戶信息+加簽+http請求+驗簽,於是呢,可以直接把它們抽象成一個公用類呀~在這里就要引入模板方法模式咯

模板方法模式

在模板模式(Template Pattern)中,一個抽象類公開定義了執行它的方法的方式/模板。它的子類可以按需要重寫方法實現,但調用將以抽象類中定義的方式進行。
這種類型的設計模式屬於行為型模式。

既然每個Handler處理,都是類似的流程,那定義一個抽象類,把查詢商戶信息,加簽,http請求,驗簽什么的,都放到里面去,儼然一個模板一樣。然后,因為有些商戶走http代理,有些又沒走代理,怎么辦呢? 定義一個抽象方法,給子類實現嘛,因為能共用就放到父類(當前的抽象類),不能共用就放到子類嘛~代碼如下:

abstract class AbstractCompanyCommonService implements ICompanyCommonService { 
     //模板方法
     Resp handlerTempPlate(req){
            //查詢商戶信息
           queryMerchantInfo();
           // 加簽
           signature();
           //http 請求
           if(isRequestByProxy()){
              httpProxy();
            }else{
              httpDirect();
             }
            // 驗簽
            verifySinature();
     }
      // Http是否走代理
      abstract boolean isRequestByProxy();
}

子類商戶A實現:

CompanyAServiceImpl extends AbstractCompanyCommonService{
    Resp hander(req){
      return handlerTempPlate(req);
    }
    //公司A是走代理的
    boolean isRequestByProxy(){
       return true;
    }

子類商戶B實現:

CompanyBServiceImpl extends AbstractCompanyCommonService{
    Resp hander(req){
      return handlerTempPlate(req);
    }
    //公司B是不走代理的
    boolean isRequestByProxy(){
       return false;
    }

策略模式

心細的讀者會發現,甚至提出疑問,你的商戶C的服務實現跟你定義的公用模板,不太一樣呢,那當然,實際開發中,不跟你定義的模板一樣太常見了,需求是產品提的嘛,又不是根據你模板提的,是代碼服務於需求的。好了,不多說啦,我使用了策略模式,來優化這個問題。

在策略模式(Strategy Pattern)中,一個類的行為或其算法可以在運行時更改。這種類型的設計模式屬於行為型模式。

策略模式理解起來其好抽象對不對?我個人理解,其實策略模式就是定義一個方法(所謂算法),給子類自己去實現。實際上就是定義個方法/接口,讓子類自己去實現。看代碼吧:

// 定義一個方法,把策略交給子類去實現。
interface ICompanyCommonService{
  Resp hander(req);
}

前面商戶A和商戶B還是不變,使用抽象類AbstractCompanyCommonService的模板,模板不滿足商戶C,商戶C只能自己去實現咯,各個子類自己去實現的行為,就是策略模式的體現呢,如下:

CompanyCServiceImpl extends AbstractCompanyCommonService{
    Res hander(req){
       //查詢商戶信息
       queryMerchantInfo();
       requestByWebservice();    
    }
    //隨意了,你都不走模板了
    boolean isRequestByProxy(){
       return false;
    }

工廠方法模式

商戶A、B、C服務怎么被管理呢,之前分別給A,B,C服務實現handler的,現在好了,都不知道怎么管理了,怎么知道調用哪個呢?別慌,解決方案是工廠方法模式

在工廠模式中,我們在創建對象時不會對客戶端暴露創建邏輯,並且是通過使用一個共同的接口來指向新創建的對象。

工廠方法模式具體實現就是:接口定義一個枚舉,每個服務實現都重新實現枚舉,設置唯一標志枚舉,再交給spring容器管理。看代碼咯:

interface ICompanyCommonService{
  Resp hander(req);
  CompanyEnum getCompanyEnum();
}

CompanyAServiceImpl extends AbstractCompanyCommonService{
    Resp hander(req){
      return handlerTempPlate(req);
    }
    //公司A是走代理的
    boolean isRequestByProxy(){
       return true;
    }
    CompanyEnum getCompanyEnum(){
     return CompanyEnum.A;
    } 
    
CompanyBServiceImpl extends AbstractCompanyCommonService{
    Resp hander(req){
      return handlerTempPlate(req);
    }
    //公司B是不走代理的
    boolean isRequestByProxy(){
       return false;
    }
    CompanyEnum getCompanyEnum(){
     return CompanyEnum.B;
    } 

來來來,工廠方法模式出爐咯:

@Component
public class CompanyServiceFactory implements ApplicationContextAware {

    private static Map<CompanyEnum, ICompanyCommonService> map = new HashMap<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, ICompanyCommonService> tempMap = applicationContext.getBeansOfType(ICompanyCommonService.class);
        tempMap.values().forEach(iCompanyCommonService ->
                map.put(iCompanyCommonService.getCompanyEnum(), iCompanyCommonService));
    }

    public Resp handler(req) {
        return map.get(CompanyEnum.getCompanyEnum(req.getCompanyFlag()).hander(req);
    }
}

最后建議

最后,不要為了使用設計模式生搬硬套,而是優化代碼過程中,發現這個設計模式剛好適用,才去用的哈。附上最后的代碼咯:

@Service
public class CompanyHandler implements RequestHandler  {
   @Autowire
   private CompanyServiceFactory companyServiceFactory;
   
   Resp hander(req){
    return companyServiceFactory.handler(req);
  }
}

個人公眾號


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM