---
# System prepended metadata

title: Code Review指導原則
tags: [sideProject]

---


<style>

html, body, .ui-content {
    background-color: #333;
    color: #ddd;
}

.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
    color: #ddd;
}

.markdown-body h1,
.markdown-body h2 {
    border-bottom-color: #ffffff69;
}

.markdown-body h1 .octicon-link,
.markdown-body h2 .octicon-link,
.markdown-body h3 .octicon-link,
.markdown-body h4 .octicon-link,
.markdown-body h5 .octicon-link,
.markdown-body h6 .octicon-link {
    color: #fff;
}

.markdown-body img {
    background-color: transparent;
}

.ui-toc-dropdown .nav>.active:focus>a, .ui-toc-dropdown .nav>.active:hover>a, .ui-toc-dropdown .nav>.active>a {
    color: white;
    border-left: 2px solid white;
}

.expand-toggle:hover, 
.expand-toggle:focus, 
.back-to-top:hover, 
.back-to-top:focus, 
.go-to-bottom:hover, 
.go-to-bottom:focus {
    color: white;
}


.ui-toc-dropdown {
    background-color: #333;
}

.ui-toc-label.btn {
    background-color: #191919;
    color: white;
}

.ui-toc-dropdown .nav>li>a:focus, 
.ui-toc-dropdown .nav>li>a:hover {
    color: white;
    border-left: 1px solid white;
}

.markdown-body blockquote {
    color: #bcbcbc;
}

.markdown-body table tr {
    background-color: #5f5f5f;
}

.markdown-body table tr:nth-child(2n) {
    background-color: #4f4f4f;
}

.markdown-body code,
.markdown-body tt {
    color: #eee;
    background-color: rgba(230, 230, 230, 0.36);
}

a,
.open-files-container li.selected a {
    color: #5EB7E0;
}

</style>
# ==**Code Review指導原則**==

- 變數相關
    1. 區域變數 
        ``` java
        String a = "";  // 錯誤示範，請使用有意義命名
        String member = ""; // 正確，定義可識別的變數名稱
        String form_id = ""; // 錯誤，要以駝峰命名
        String formId = ""; // 正確
        ```
    2. 常數
        ```java
        private final String init = ""; // 錯誤，常數請用大寫
        private final String INIT = ""; // 正確
        private final String initFirst = ""; // 錯誤，常數請不要用駝峰命名
        private final String INIT_FIRST = ""; // 正確
        ```
        
    3. function命名
        ```java
            
             // 請勿使用底線命名   
            public void get_id(){
                // some code here
            }
            
            // 請使用駝峰命名
            public void getId (){
                // some code here
            } 

            // 呈第1點，參數需要有意義的命名
             public void getId(int a){  // 請勿取a這種奇怪的命名
                // some code here 
             }
        ```
    ---
- 善用第三方套件，防止NullPointException :
    
    1. StringUtils
        - 主要用途:針對是否為空字串、字串相等、任一字為空...等第三方套件
        
        ```java
        String item1 = someObject.getSomeString();
        String item2 = someObject.getSomeString();

        // 1.物件比較使用StringUtils，避免NPE
        StringUtils.equals(item1,item2);
        
        // 2. 倘若100%確定任一字串有值，即可使用原生equlas，但僅限有值的放在前面
        String item11 = "item";
        String item22 = someObject.getSomeString();
        item11.equals(item22);
            
        ```
    2. CollectionUtils 
        - 主要用途:針對List的元素操作
        ```java
        List aList = someObject.getList();
        List bList = someObject.getList();
        
        // 1. 物件是否為空使用
        CollectionUtils.isEmpty(aList);
        // 2. 是否包含XXX物件 
        CollectionUtils.containsAny(xxx);    
        ```
        
   3. MapUtils 
       - 主要用途:針對Map元素進行操作，並可視情況給予Default值
        
        ```java
            public void getMapValue(Map<k,v> map) {
               // 取int值
                Integet intValue = MapUtils.getInteger(map,"intKey" , 0);
              // 取String值  
                String stringValue = MapUtils.getString(map,"stringKey","預設值")
              // ... 其餘用法請參考API文件
            }
        ```
        
        ==*上述三個範例套件，主要都是防止Npe操作用，視程式邏輯使用，不一定要每次使用*==
    
        ---
        
- 實作寫法篇 : 
    1. 禁止變數濫用
    
        ==Example1==
            
        ``` java
         public String function1(){
             String a = someObject.getString();
             // 其他業務邏輯
             
             return a;
         }
        ```
       
       上述這個案例，a只有使用一次，就可以不必再單獨宣告物件
       
        ==Example2==
        ```java
        public String funtion2(){
            String a = somObject.getString();
            // 其他業務邏輯
            if(StringUtils.isBlank(a)){
                a = anthoerObject.getString();
            }
            return a;
        }
        ```
        上述這第二個案例，a因有超過一處的地方使用，才需要單獨宣告出來
    
    2. 禁止Exception濫用
    
        ==Example1==
        ```java
        public void testException1(){
            // some code here
            
            // sudo code
            if(Boolean result){
                throw new Exception(exception.getMessage);
            }
        }
        ```
        上述這個案例，直接丟出最大的Exception，不是一個好選擇。應需視業務情況，選擇拋出相對應的Exception
        
        ==Example2==
        ```java
        public void testException2(){
            // some code here
            try{
                // 針對DB進行資料處裡
                xxxDao.insert(Object);
            } catch(DataAccessException e){
                // 發生資料重複的例外，執行例外流程
                log.error("insert error ! message :{}" , e.getMessage,e);
                throw e;
            } catch(ConnectException e){
                // 例外流程
                throw e;
            }
            
        }
        ```
        上述這個案例，針對不同的例外類別，應有不同處理流程。不應該一視同仁，全部丟出最大的Exception
        
        3.迴圈裡面禁止針對==DB進行操作== : 
    
        ==Example1==
        
        ```java
        public void testLoop1(){
            List<?> result = new ArrayList();
            for(Element e : dataList){
                // some code here ... 
                // use select sql
                SomeObject resultObject = xxxDao.findByElement(e);
                if(Boolean Result){
                    result.add(resultObject);
                }
            }
        
        }
        ```
        上述這種情境，會在迴圈裏面，跑多次的DB Select，此為禁止行為!
        應改用sql cmd解決
        
        ==Example2==
        ```sql
        select * from xxxTable where filter in (elements);
        ```
        再接續處裡後面邏輯
        ```java
            public void testLoop2(){
                List<?> result = new ArrayList();
                
                // 取消for loop
                List<?> resultList = xxxDao.getList(); // dao背後的sql cmd如同上面的範例2
                // some code here
            }
        ```
        
        
    4. 交易控管 : 
        針對不同的交易行為，因設定好交易控管(==Transactional==)，但禁止設定無效交易
        
        ==Example==
        ```java
            @AutoWired 
            private Aservice aService;
            @AutoWired
            private Bservice bService;

            @Transactional
            public void testTransition1(){
                // some code here 
                // A Service針對DB進行資料管控
                aService.insert(someObject);
                // B Service針對DB進行資料管控
                bService.insert(someObject);
            }

        ```
        
        依上述這個範例而言，將交易控管綁在主流程，這樣只要testTransition1這個方法，執行過程中，有任何例外，所有的交易就會回滾。
        
        ```java
            @Transactional
            public void aServiceMethod1(someObject){
                aDao.insert(someObject);
            }
        ```
        上面這個[aServiceMethod1]所綁的交易，則為無意義的交易控管，因為已在「另一個Class裡的testTransition1」綁住交易。
        
        事務回滾範圍參考 : 
        https://matthung0807.blogspot.com/2020/11/spring-transactional-methods-call-rollback-boundaries.html
        
    5. SOLID概念(非強制，供設計建議) : 
        以單一職責舉例，盡量避免單一method負責處理多項業務功能

        ==Example1==
        ```java
            // 這是一個購票網站的方法
            public Object testSolid1(Map<k,v> map){
                // step 1 確認是否已登入會員
                // step 2 確認參數是否正確
                // step 3 選擇購票交易類型、日期、時間
                // step 4 DB交易
                // step 5 返回交易結果
            }
        ```
        以上述的流程概念，應將不太相干的業務行為，拆分成另一獨立method，甚至是Class
        
        ==Example2==
        ```java
            public Object testSolid2(Map<k,v> map){
                // step 1 確認是否已登入會員
                aCheckService.checkIsLogin();
                // step 2 確認參數是否正確
                bService.checkParameter();
                // step 3 選擇購票交易類型、日期、時間
                this.choseTicket();
                // step 4 DB交易
                this.insertData();
                // step 5 返回交易結果 
                return someObject;
            }
        ```
        SOLID Design Pattern參考資料 : 
        https://medium.com/%E7%A8%8B%E5%BC%8F%E6%84%9B%E5%A5%BD%E8%80%85/%E4%BD%BF%E4%BA%BA%E7%98%8B%E7%8B%82%E7%9A%84-solid-%E5%8E%9F%E5%89%87-%E7%9B%AE%E9%8C%84-b33fdfc983ca
        
    5. DB交易
        資料管控是最重要的地方，針對交易行為時，需要嚴格管控!
        
        ==Example1==
        
        ```sql
            /* 在下查詢語法的時候，盡量帶著where條件，避免撈取資料過大，浪費資源 */
            -- 資料查詢
            select * from xxxTable where xxxColumn = 'xxx';
        ```
        
        ```sql
         /* sql在下更新語句時，絕對要帶入where條件，避免直接將整個DB的資料都給update掉 
            刪除的語法亦是如此
         */
           update xxxTable
           set aColumn = 參數1,
               bColumn = 參數2
           where xxxColumn = 參數3
        ```
        而在Java中，可以針對新增、修改、刪除的DB交易行為，將其異動結果，返回一個int的值
        
        ==Example2==
        
        ```java
            public void testData2(){
                // 示範:新增
                int saveResult = xxxDao.save();
                // 預期 saveResult應為1筆，如果不為1筆，要走異常處理流程
                if(saveResult != 1){
                    throw new xxxException("db error!")
                }
                
            }
        ```
        上述用意為，程式設計師要確保資料的異動結果，是在期望值內，若不符預期，則需善用資料回滾的機制
        
        6.Log資訊機制
        log是記錄系統資訊流程很重要的東西，但需善用，避免增加系統負荷
        
        ==Example1==
        ```java
            public void testLog(){
                // some code here
                
                if(Boolean result){
                    // 僅紀錄某些特定字串
                    log.info("some message here");
                }
                
                if(Boolean result){
                    // 除特定字串外，需額外紀錄某些資訊，用 {}及, 進行串接
                    log.info("some object :{}",someObject);
                }
                
                if(Boolean result){
                    try{
                    // some code here
                    }catch(xxxException e){
                    // 此處因有例外發生，除了例外訊息外，建議也將例外類別印出，以利後續異常排除
                    log.error("error ! message :{}",e.getMessage,e)
                    }
                }
        ```
        *注意 ：使用原則*
            
            1. log所要記錄的內容，資訊有什麼幫助？真的有需要嗎？
            2. 大部分的日誌層級，皆使用info及error即可滿足，除非有特殊需求，要使用到warn等等，需於程式註解說明
            3. 避免無意義的log資訊，如下方範例
        
        ```java 
            public ResponseBondy testLog2(){
                String message = "some information";
                // 某一物件
                ResponseBondy res = new ResponseBondy(); 
                res.setMessage(message);
                log.info(message); // 無意義，可刪除
                // 此處回傳的物件，已有將message塞入，故上一行的log紀錄為多餘
                // 除非有特殊需求，需於程式端進行註解
                return res;
            }
        ```
        
        ==Example2==
        大部分的使用情境為排程 || 監聽器
        ```java
            // 以排程執行為範例
            public void testSchedule(){
                try{
                log.info("xxx排程執行開始");
                // 排程執行方法    
                xxxSchedule.someMethod();
                }catch(xxxEcception e){
                    log.error("Schedule error ! message :{}",e.getMessage,e);
                    // some code here
                }finally{
                    log.info("xxx排程執行結束");
                }
            }
        ```
        以上述為例，為確保排程程式有正確運行，故於接口一進入，即info執行資訊。而會設計finally的原因為，確保排程有成功執行完成(無論是否例外)
        
    7. Loop相關 
        盡量以forEach取代傳統for迴圈
        
        ==Example==
        ```java
            public void testLoop(){
                // 寫法1
                for(int i = 0 ; i < 10 ; i++){
                    // some code here
                }
                
                // 寫法2
                for(Element e : eList){
                    // some code here
                }
            }
        ```
        在可允許的範圍內，以寫法2為優先選擇，原因如下 :
        
        - 執行效能較優
        - 傳統for loop的閱讀性較差 -> 上方的 ==**int i**==，若無註解，較難直觀閱讀其用意
        - 傳統for loop沒有控制好，易有indexOutOfBoundException

---

- 註解相關

    1. class/interface註解 : 於類別上方使用 /***/方式，說明類別用途
    
        ![](https://hackmd.io/_uploads/BkkvRnP3h.png)
        
    2. method註解 : 於方法上使用/***/方式，說明方法用途、參數/回傳資訊
        ![](https://hackmd.io/_uploads/S15yJpv22.png)
    3. 流程註解 : 紀錄系統執行流程的意義
        ![](https://hackmd.io/_uploads/HkMqJpP3h.png)
    4. 物件詳細註解 : 紀錄物件內部的資料資訊
        ![](https://hackmd.io/_uploads/HJ7oZaw2h.png)
        
---
- 其他注意事項
    - Spring Bean注入方式，使用建構子注入，禁止使用@Autowired
    - log紀錄以info層級為主，僅有捕捉例外時，使用log.error


---

- 版本控制
    
    1. git commit message 格式
    
        ==Example==
        
        ```text
            「commit類別」: 此次commit主要功能
                1. 附加說明1
                2. 附加說明2
                3. 附加說明3
            「功能名稱」+「底線」+「分支名稱」
        ```
        上述的附加說明，若主要功能可說明完成，即可忽略
        
         commit類別說明
         
         - feat : 新增加的功能/需求
         - reafactor : 程式碼重構
         - docs : 新增註解
         - format : 程式碼排版
         - fix : Bug修復
         - codeReview : 程式碼review後的修改
         - entity : 僅新增與DB對應的實體類別

        ==實際Example==
        
        ```text
            feat: 新增jwt登入驗證功能
                1. security配置檔案配置
                2. token過期時間reSet
            edmFlow_Harvey_issue0814
        ```
    2. commit規範:
    
        - 嚴禁一次commit多筆資料(Ex:超過10筆)
        - 依功能別commit，禁止一次commit多功能檔案
        - 若有同一功能，多筆檔案需commit，請拆解成模組commit(Ex: 查詢模組/新增模組)
        - 嚴禁commit message非依格式commit

    3. Branch規範:
        
        - 創建本地Branch : 測試分支為base，拉出本地Branch
        - Merge流程 : localBranch -> devBranch ->masterBranch

        ==**特別注意**==
         
            1. 所有的localBranch，必須完成測試後，才能申請MergeRequest
            2. 所有Merge到devBranch的code，皆必須是測試完成無Bug

    4. Create Merge Request (GitLab)
        
        - 每當分支需合併時，需開立merge request
        - Assigner請指向Team Leader
        - Reviewer請指向codeReview的人
        - Reviewer所comment的問題，僅有Reviewer能夠關閉comment，developer禁止關閉

---

==**Format**==
    
- 採google java格式 
    
- https://blog.miniasp.com/post/2022/09/03/Google-Java-Style-Guide-for-Eclipse-STS4-and-IntellJ-IDEA

- https://github.com/google/styleguide/blob/gh-pages/intellij-java-google-style.xml