代碼評審歪詩

来源:http://www.cnblogs.com/yukaizhao/archive/2017/09/01/code-review-20-words.html
-Advertisement-
Play Games

驗幻空越重,命循頻異長。依輪線日簡,接偶正分壯。言歡空月蟲,明勛品宜昌。依倫先日賤,潔偶正粉妝。 ...


        賈言      
       

驗幻空越重,

       

命循頻異長。

       

依輪線日簡,

       

接偶正分壯。

     
       

言歡空月蟲,

       

明勛品宜昌。

       

依倫先日賤,

       

潔偶正粉妝。

     

賈言

架構師說, 用20個字描述代碼評審的內容, 自省也省人。由於是一字一含義, 不連貫, 為了增強趣味性, 每句都增加對應的歪解。只是對常見評審的描述, 不盡之處,歡迎補充!

  驗幻空越重  -- 言歡空月蟲

  驗:公共方法都要做參數的校驗,  參數校驗不通過明確拋出異常或對應響應碼。

     
  1.     java bean驗證已經是一個很古老的技術了, 會避免我們很多問題, 可參考:    
              http://beanvalidation.org/ http://www.infoq.com/cn/news/2010/03/javaee6-validation https://www.sitepoint.com/using-java-bean-validation-method-parameters-return-values/      
  2.  
  3.     在介面中也明確使用驗證註解修飾參數和返回值, 作為一種協議要求調用方按驗證註解約束傳參, 返回值驗證註解約束提供方按註解要求返回參數  

  幻:在代碼中要杜絕幻數,幻數可定義為枚舉或常量以增強其可讀性

  空:要時刻警惕空指針異常

     
  1.     常見的 a.equals(b) 要把常量放到左側  
  2.  
  3.     aInteger == 10 如果 aInteger 為空時會拋出空指針異常  
  4.  
  5.     不確認返回集合是否可為空時要做非空判斷, 再做for迴圈  
  6.  
  7.     使用空對象模式, 約定返回空集合, 而非null  
  8.  
  9.     使用StringUtils判斷字元串非空          
         

  越:如果方法傳入數組下標作為參數,要在一開始就做下標越界的校驗,避免下標越界異常

  重:不要寫重覆代碼,重覆代碼要使用重構工具提取重構

 

  命循頻異長 -明勛品宜昌

  命:包/類/方法/欄位/變數/常量的命名要遵循規範,要名副其實,這不但可以增加可讀性,還可以在起名的過程中引導我們思考方法/變數/類的職責是否合適

有意義很重要, 典型無意義命名:


public static final Integer CODE_39120 = 39120;
public static final String MESSAGE_39120 = "[包裹]與[庫房號]不一致,確定裝箱?";
public static final Integer CODE_39121 = 39121;
public static final String MESSAGE_39121 = "[包裹]與[箱號]的承運類型不一致,確定裝箱?";
 
Rule rule1 = request.getRuleMap().get("1050");

CODE_39120這個名字和幻數沒多大區別。

  循:不要在迴圈中調用服務,不要在迴圈中做資料庫等跨網路操作

  頻:寫每一個方法時都要知道這個方法的調用頻率,一天多少,一分多少,一秒多少,峰值可能達到多少,調用頻率高的一定要考慮性能指標,考慮是否會打垮資料庫,是否會擊穿緩存

  異:異常處理是程式員最基本的素質,不要處處捕獲異常,對於捕獲了只寫日誌,沒有任何處理的catch要問一問自己,這樣吃掉異常,是否合理

下麵是一個反例, 在導出文件的controller方法中做了兩層的try...catch, 在catch塊中記錄日誌後什麼都沒做, 這樣用戶看不到真正想要的內容, 研發也只有看日誌才能發現錯誤, 而“看日誌”, 通常只有業務方反饋問題時才會看, 就會導致研發人員發現錯誤會比現場人員還會晚。


@RequestMapping(value = "/export")
public void export(CityRelationDomain condition, HttpServletResponse response) {
   ZipOutputStream zos = null;
   BufferedWriter bufferedWriter = null;
   try {
      condition.setStart(0);
      condition.setSize(MAX_EXPORT_LINES);
      List list = cityRelationService.getOrdersByCondition(condition);
      response.setCharacterEncoding("GBK");
      response.setContentType("multipart/form-data");
      response.setHeader("Content-Disposition", "attachment;fileName=export.zip");
      zos = new ZipOutputStream(response.getOutputStream());
      bufferedWriter = new BufferedWriter(new OutputStreamWriter(zos, "GBK"));
      bufferedWriter.write("訂單類型編碼,始發城市-省,始發城市-市,目的城市-省,目的城市-市");
      ZipEntry zipEntry = new ZipEntry("export.csv");
      zos.putNextEntry(zipEntry);
      for (CityRelationDomain domain : list) {
         try {
            bufferedWriter.newLine();
            bufferedWriter.write(CSVExportUtil.trans2CSV(domain.getOrderCode()));
            bufferedWriter.write(',');
            bufferedWriter.write(CSVExportUtil.trans2CSV(domain.getProvinceNameFrom()));
            bufferedWriter.write(',');
            bufferedWriter.write(CSVExportUtil.trans2CSV(domain.getCityNameFrom()));
            bufferedWriter.write(',');
            bufferedWriter.write(CSVExportUtil.trans2CSV(domain.getProvinceNameTo()));
            bufferedWriter.write(',');
            bufferedWriter.write(CSVExportUtil.trans2CSV(domain.getCityNameTo()));
         } catch (Exception e) {
            e.printStackTrace();
         }
      }
      bufferedWriter.newLine();
           bufferedWriter.flush();
           zos.closeEntry();
           bufferedWriter.close();
   } catch (Exception e) {
      e.printStackTrace();
      logger.error("導出CSV文件異常");
   } finally {
      try {
         if (zos != null) {
            zos.close();
         }
         if (bufferedWriter != null) {
            bufferedWriter.close();
         }
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

  長:如果一行代碼過長,要分解開來;如果一個方法過長,要重構方法;如果一個類過長要考慮拆分類

  依輪線日簡 -依倫先日賤

  依:如果調用了外部依賴,一定要搞清楚這個外部依賴可以提供的性能指標,最好約定SLA

  輪:不要重覆造輪子,如果已經有成熟類庫實現了類似功能,要優先使用成熟類庫的方法,這是因為成熟類庫中的方法都經過很多人的測試驗證,通常情況下我們自己實現的質量最大等同於成熟類庫的質量。

  線:要註意我們的jsf服務, web應用,消費消息的worker都是多線程環境,要註意線程安全問題,最典型的HashMap, SimpleDateFormat, ArrayList是非線程安全的,另外如果使用Spring自動掃描服務,那麼這個服務預設是單例,其內部成員是多個線程共用的,如果直接用成員變數是有線程不安全的。

兩個典型的錯誤代碼片段:

  1)無視SimpleDateFormat非線程安全


@Service
public class AService {
    private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd");

    public void doSomething() {
        //use FORMAT
    }
}

 

  2)使用Service成員變數


@Service
public class BService {
    private Pojo b;

    public void doB() {
         b = getB();
         process(b);
    }
}

 

  日:列印日誌和設定合理的日誌級別,如有必要要添加if條件限定是否列印日誌,在日誌中使用JSON序列化,生成長字元串的toString()都要做if限定列印,否則配置的日誌級別沒達到,也會做大量字元串拼接,占用很多gc年輕代記憶體.  另外一定要通過log4j列印日誌而不是直接把日誌列印到控制台。

 

典型錯誤示例:


@Service
public class FooService {
    private static final Logger LOGGER = LoggerFactory.getLogger(FooService.class);

    public void doFooThing(Foo foo) {
        LOGGER.debug("get parameter foo {}", JSONObject.toString(foo));
        try {/*do something*/} catch (Exception ex) {ex.printStackTrace();}
    }
}

 

  簡: 儘可能保持整體設計的簡潔, 方法實現的簡潔, 要根據情況使用記憶體緩存, redis 緩存, jmq 非同步處理。 這裡的簡需要把握好分寸。

  接偶正分壯 - 潔偶正粉妝

  接:介面是用來隔離變化的,如果一個業務有幾種不同的形態,但都有相同的處理,那麼可以定義介面來隔離業務形態的不同,在服務調用處,通過業務類型欄位來獲得不同的服務類。  而不要實現一個類,然後在類的各個方法中都根據業務類型做if else或更複雜的各種判斷。

典型示例:

  做法1:

public interface BarService {    void doBarThing(Bar b);
    
    void doBarFatherThing(Bar b);
}
public class BarServiceImpl  implement BarService{
    public void doBarThing(Bar b) {
        if (b.getType() == BarType.A) {
            //do some logic
        } else (b.getType() == BarType.B) {
            //do some B type logic
        }
        //do other doBarThing logic
    }
    
    public void doBarFatherThing(Bar b) {
        if (b.getType() == BarType.A) {
            //do some logic
        } else (b.getType() == BarType.B) {
            //do some B type logic
        }
        //do other doBarFatherThing logic
    }
}

 

做法 2 :

public interface BarService {
    void doBarThing(Bar b);
    
    void doBarFatherThing(Bar b);
}
public class BarServiceFactory {
    public BarService getBarService(BarType type) {
        // get bar service logic
    }
}
//如果有公共邏輯就定義, 沒有就不定義
public class BaseBarService implement BarService {
    public void doBarThing(Bar b) {
        //do other doBarThing logic
    }
    
    public void doBarFatherThing(Bar b) {
        //do other doBarFatherThing logic
    }
    
}
public class TypeABarService extends BaseBarService  implement BarService {
    public void doBarThing(Bar b) {
        // doATypeThing
        super.doBarThing(b);
    }
    
    public void doBarFatherThing(Bar b) {
        // do bar type A service
super.doBarFatherThing(b); //如果需要就調用, 不需要就不調用父類
    }
    
}

 

  做法2的好處是將不同類型的邏輯解耦,各自發展,不會相互影響,如果添加類型也不必影響現有類型邏輯。

 

  偶:認識系統之間的耦合關係,通過同步數據來做兩個系統之間的交互是一種很強的耦合關係,會使數據接收方依賴於數據發送方的資料庫定義,如果發送方想改數據結構,必須要求下游接收方一起修改;通過介面調用是一種常見的系統耦合關係,介面的提供方要保證介面的可用性,介面的調用方要考慮介面不可用時的應對方案; mq消息是一種解耦的方法,兩個系統不存在實時的耦合關係。但是mq解耦的方式不能濫用,在同一系統內不宜過多使用mq消息來做非同步,要儘可能保證介面的性

  能,而不是通過mq防止出問題後重新消費。

 

  正:模塊之間依賴關係要正向依賴,不能讓底層模塊依賴於上層模塊;不能讓數據層依賴於服務層也不能讓服務層依賴於UI層;也不能在模塊之間形成迴圈依賴關係。

 

  分:分而治之,複雜的問題要分解成幾個相對簡單的問題來解決,首先要分析出核心問題,然後分析出核心的入參是什麼,結果是什麼,入參通過幾步變化可以得出結果。

 

  壯:時刻註意程式的健壯性,從下麵三個方面實踐提升健壯性:

  1)契約,在設計介面時定義好協議參數,併在實現時第一時間校驗參數,如果參數有問題,直接返回給調用方; 如果出現異常情況, 也按異常情況約定應對策略

2)  考慮各種邊界條件的輸出, 比如運單號查詢服務, 要考慮用戶輸入錯誤運單時怎麼返回, 有邊界的查詢條件, 如果用戶查詢條件超過邊界了, 應該返回什麼

  3   )為失敗做設計,如果出問題了有降級應對方案。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 之前在學習python的時候有整理過python異常處理的文章,不夠簡單也不夠完整,所以決定再整理一篇,算做補充。 http://www.cnblogs.com/cmt110/p/7464748.html python shell 打開一個不存在的文件abc.txt 文件,當系統找不到abc.txt ...
  • 2.copyOf:複製出新的數組,複製長度由 newLength 決定,長度可大於被覆制數組的長度 copyOfRange:複製指定下標範圍內的值 3.sort:預設進行升序排序,可自定義排序 4.toString:數組toString deepToString:二維數組toString 5.equ ...
  • list是一種有序的集合,可以隨時添加和刪除其中的元素。 知識點: . 創: 增: list是一個可變的有序表,所以,可以往list中追加元素到末尾: 也可以把元素插入到指定的位置,比如索引號為1的位置: 刪: 要刪除list末尾的元素,用pop()方法: 要刪除指定位置的元素,用pop(i)方法, ...
  • re模塊包含對 正則表達式。本章會對re模塊主要特征和正則表達式進行介紹。 什麼是正則表達式 正則表達式是可以匹配文本片段的模式。最簡單的正則表達式就是普通字元串,可以匹配其自身。換包話說,正則表達式’python’ 可以匹配字元串’python’ 。你可以用這種匹配行為搜索文本中的模式,並且用計算 ...
  • 註:本文轉載自趙學智@行勝於言《什麼是對象,為什麼要面向對象,怎麼才能面向對象?》 地址:http://www.cnblogs.com/seesea125/archive/2012/04/03/2431176.html 一、 面向對象,這個對象到底是什麼? 這個對象不是C#中的實例,C#中我們把一個 ...
  • 1.抽象的產品類 2.抽象的工廠類 3.客戶端的程式 至此,以上都是應用抽象工廠基本不變的代碼。接下來就是具體的實現類,也就是根據變化的需求給出變化的代碼 4.具體產品類 5.具體工廠類 6.應用 ...
  • 伺服器設置 1. 安裝64位JDK; 2. 設置Linux文件系統為Ext4 3.開啟2181,7771,7772防火牆埠 源碼編碼 1. 安裝Maven 2. 安裝OpenSesame 下載地址:https://github.com/alibaba/opensesame.git,將openses ...
  • 使用裝飾者模式,可以動態的給一個對象添加一些額外的職責。這適用於,我們只希望給某個對象而不是整個類添加一些功能的場景。通過使用含有某個特定功能的類來“包裹”原始的類,提供給原始的類某些它本身不具備的特性。比如,我們有一杯“茉莉茶”,現在加上一顆“檸檬”,那我們就有了一杯“檸檬茉莉花茶”。“檸檬”作為... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...