---
# System prepended metadata

title: Ch3-1、2

---

# Ch3-1、2

#### 前言
##### 1. Ch2-了解為何需要重構(How)(小複習)
- 目的:
 使軟體內部程式更**容易理解、修改、加速開發流程**。
- 時間:
 不是另外特別花時間重構，遇到 **feature** 時來做。
- 成效:
 有重構**長時間**而言，整體軟體品質好的**容易修改、理解**。
<!-- ~~ 希望軟體長期是紅線樣子->長期重構可以加速軟體開發的進程。(功能越多.開發時程還是很順。不會中後期因為架構不好很難增加新功能、修改很辛苦)~~ -->
 ![](https://i.imgur.com/gxJDFiS.png)

##### 2. Ch3-使用時機(When)
- 注意:
 這章講的不是一個標準。
 作者:沒有一個標準比得上一個見識廣博者的直覺。
- 培養敏感度、判斷力的章節
 提出跡象=>這裡有一個可以用重構來解決的問題。

-------------
#### 一、Mysterious Name 神秘的名稱
##### 優缺點: 
 - (優)好名稱明確傳達如何被使用。
 - (優)省下好幾個小時的疑惑，不用再去看實作的程式碼。 
 - (缺)很難一次取對名稱。
 一開始可能不清楚怎麼命名，隨著時間逐漸知道關節應該長什麼樣子，而不斷改善程式
 - (缺)人通常很害怕改變名稱認為不用這麼麻煩。

##### 目的&效果
作者最常做的重構 => 重新命名
 1. 將難懂的名稱變清楚來簡化程式。
 2. 重新命名不僅僅是改變名稱.當你無法幫某個東西想個好名稱.往往代表深層設計不良。

##### Tips
- 修改時機:
 當知道更好的名稱時.立刻改動.之後需要再次釐清來龍去脈。
- 修改地方:
 function、param、class
- 命名方法:
 寫下註解來說明函式的用途，再把註解變成一個名稱
 (根據函式的目的、做什麼來命名，而不是如何如何做那件事情)
- 作法:
 1.宣告成你要的樣子
 2.使用副本把新舊名稱都放上去
 (移除時確保內文沒有使用它.或先多寫測試完再移除)
 3.每個引用舊方法的地方更新
 4.測試
 =>分別進行修改，用不同步驟分別處理它
(分區塊一直重複上面3個步驟)
(小步驟代表每個步驟容易出錯的地方，切成小步驟修改出錯率降低減少工作量)


##### 範例
- Change Function Declaration 147 改函式宣告 (ch6)
    ```
    // 名稱過度簡寫
    function circum(radius) {...}  //環繞(半徑)

    // 1.修改宣告名稱
    function circumference(radius) {...}  //圓周(半徑)

    // 2.把使用到的名稱都修改
    // 3.測試
    ```
    
- Rename Variable 162 (ch6)
-最容易改變的變數是區域變數。
-作法:使用副本藉由複製方式逐漸修改名稱。
(副本>舊名稱改成新名稱>測試(失敗回去舊名稱))
    ```
    const companyName = "Acme Gooseberries";
    const cpyNm = companyName; 
    ```
    
- Rename Field 285 更改欄位名稱 (ch9)
-影響比較大範圍在細拆。
-class.資料結構、欄位名稱
Fred Brooks: 如果你讓我看一下流程圖，再把圖表收起來，我會繼續困惑不解。但是如果你讓我看一下表格，我通常就不需要流程圖，他們會歷歷在目。(資料結構是了解來龍去脈的關鍵)

    目標: 希望把 name 換成 title
    ```
    const organization = {name: 'Acme Gooseberries', country: "GB"}; // name 換成 title
    
    class Organization{
      constructor(data){
        this._name = data.name;
        this._country = data.country;
      }
      get name()       {return this._name;}
      set name(aString) {this._name = aString;}
      get country()    {return this._country;}
      set country(aCountryCode) {this._country = aCountryCode;}
    }
    ```
    
    step1. 先改名稱: _name 換成 _title
    ```
    class Organization{
      constructor(data){
        this._title = data.name;  // 改名稱。(原)this._name = data.name;
        this._country = data.country;
      }
      get name()       {return this._title;}  // 改名稱。(原) {return this._name;}
      set name(aString) {this._title = aString;}  // 改名稱。(原) {this._name = aString;}
      get country()    {return this._country;}
      set country(aCountryCode) {this._country = aCountryCode;}
    }
    ```

    step2. 保留新舊名稱
    -class 底層的資料可能被很多地方使用。
    用物件容納資料.而不是紀錄。(Encapsulate Record  188 ch7)    
    ```
    class Organization{
      constructor(data){
        // 2. 新舊都保留(title 優先)
        // 原: this._title = data.name;
        this._title = (data.title !== undefined)? data.title: data.name;
        this._country = data.country;
      }
      get name()       {return this._title;}
      set name(aString) {this._title = aString;}
      get country()    {return this._country;}
      set country(aCountryCode) {this._country = aCountryCode;}
    }
    ```
    
    step3. 一一檢查使用到的地方換成新的名稱
    ```
    const organization = new Organization({title: 'Acme Gooseberries', country: "GB"});
    ```
    
    step4. 移除舊名支援
    ```
    class Organization{
      constructor(data){
        // 2. 移除舊名
        // 原: this._title = (data.title !== undefined)? data.title: data.name;
        this._title = data.title;
        this._country = data.country;
      }
      get name()       {return this._title;}
      set name(aString) {this._title = aString;}
      get country()    {return this._country;}
      set country(aCountryCode) {this._country = aCountryCode;}
    }
    ```
    
    step5. 建構式、資料使用新名稱後，開始修改存取函式。
    ```
    class Organization{
      constructor(data){
        this._title = data.title;
        this._country = data.country;
      }
      get title()       {return this._title;}  // 改名稱。(原) get name() 
      set title(aString) {this._title = aString;}  // 改名稱。(原) get name(aString)
      get country()    {return this._country;}
      set country(aCountryCode) {this._country = aCountryCode;}
    }
    ```
    
#### 補充: 如何命名
##### 目的: 
一眼看出含意、種類
(*可讀性、一致性_相同的規則命名整個程式的變數)
##### 類型與使用情境:
一般可以使用: 英文、數字(不可開頭)、特殊符號$_。
- Pascal Case 大駝峰
(ex: UserName、FileName)
component 檔名、typeScript 的 interface
- Camel Case 小駝峰 (最常用)
(ex: userName、fileName)
- Snack Case _分離 (最常用)
(ex: user_name、file_name)
- Kebab Case -分離
(ex: user-name、file-name)
    1. html的裡面的class和id
    ```
    <div class="col-md-6" id="user-password"></div>
    ```
    2. restful api的分隔符號
    ```
    http://localhost/api/order-list
    ```
- Screaming Case 
(ex: USER_NAME、FILE_NAME)
    1. 常量變數(constant variable)
    ```
    MAX_EXECUTION_TIME = 60;
    ```
    2. enum 枚舉
    數學、計算機科學中”集合的名詞” (ex: Level、Week)
    ```
    enum Level {
      LOW,
      MEDIUM,
      HIGH
    }
    ```
- 私有變數
-前面加「_」。
-多使用在物件導向程式。函式、屬性上可以使用。
-目的:警示開發者不要使用該私有變數，不要動到!
    ```
    class Organization{
      constructor(data){
        this._title = data.title;
        this._country = data.country;
      }
    }
    ```
> 補: #開頭，私有變數有些有強制有些沒有
> https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes/Private_class_fields


- 型態命名
    1. object 取名 userObj。
    2. array 取名 userList、userArray。
    (比命名 users 更清楚相關訊息，不是取型態 userString、strUser)
    3. boolean 取名 isLoading。
        - is/has/can + 名詞或形容詞
        (isUser、hasName、canSignup、hidden、available)
        - enable/disable + 動詞
        (enableOrder、disableUpate)
        - function 返回 boolean，前面可以加 check
        注意下面第10項
        (checkHasName()、checkIsUser())


##### 命名方式參考:
1. 團隊共識。
2. 開發時，有更適合的變數名稱立刻更改，不要忌諱隨時改名。
3. 在清楚表達的前提下，名稱越短越好。
4. 區分單複數。
 單數 user。
 複數 users、userArray、userList、userCollection。
5. 選擇好單字使用到底。
 ex: user 換成 member、get 換成 fetch、retrieve。
 
6. 變數組成避免單一動詞，加一點形容詞、名詞。
     ex: create = 1
     > 使用「動詞-名詞-形容詞-副詞」命名，可以清晰、簡潔了解函式或變數的目的和行為。
     > 動詞-函式或變數的動作 (ex:get, fetch, calculate, filter)
     > 名詞-它們的含意 (ex:data, information, results, options)
     > 形容詞-它們的屬性、特徵 (ex:sorted, filtered, current, previous)
     > 副詞-它們的方式、特定條件或環境 (ex:quickly, easily, efficiently, correctly)
     > --> ex：getData、processTask、filterResults、sortArray。
7. 有異議或公認的縮寫。
 -專案中有獨有的縮寫.利用註釋、準備備用語集，來幫助新成員理解。
 ex: id(identity)、int(integer)。
 ex: order system 裡面的 user，
 不好的縮寫:  os_user、soUser。
 比較好的: system_order_user或 systemOrderUser。
8. 使用無意義的單字、字母時，確保作用域很小。
 作用域越廣，名稱需要越清楚描述變數的意思。
 ex: i, j, data，使用在(for loop、if block)
9. 具備可搜尋性.盡量不要使用無意義的單字
 ex: data、temp、value，使用太多沒有分辨性、意義。
10. 選擇不會模擬兩可的單字
checkMessage: 可能被解讀成很多訊息
-檢查訊息是否存在 existsMessage
-檢查格式是否正確 isMessageFormatValid
-詢問伺服器是否有新訊息 queryNewMessage
-過濾滿足某些條件的訊息 takeMessageIf
![](https://i.imgur.com/ttfjI93.jpg)


##### 參考資料
- https://medium.com/程式愛好者/變數命名-f53cd1115076
- coding style
https://ithelp.ithome.com.tw/articles/10197116
- https://hackmd.io/@HITSC/NamingConvention
- https://engineering.linecorp.com/zh-hant/blog/code-readability-vol2-ch/
-------------
#### 二、Duplicated Code 重複的程式碼
在一個以上的地方看到重複的程式，重構是為了減少重複看.比較程式之間的差異
##### Extract Function (一樣)(ch6)
90%情況可以使用這個縮短函式
- 有些判斷情況: 程式長度超過螢幕、作者認為超過6行、重複程式(但比較推下面方式)
- ***意圖與實作分離** => **需要花時間理解的程式碼應該拉成函式**
```
function printOwing(invoice) {
  printBanner();
  let outstanding  = calculateOutstanding();

  //print details
  console.log(`name: ${invoice.customer}`);
  console.log(`amount: ${outstanding}`);  
}

// Refactor to:

function printOwing(invoice) {
  printBanner();
  let outstanding  = calculateOutstanding();
  printDetails(outstanding); //實作獨立

  function printDetails(outstanding) {
    console.log(`name: ${invoice.customer}`);
    console.log(`amount: ${outstanding}`);
  }
}
```

##### Slide Statements (類似)(ch8)
- 類似、相關的程式放在一起容易理解，中間明確隔開的函式。
ex: 宣告的變數放在使用的放面，不是統一放在最上面。
```
const pricingPlan = retrievePricingPlan(); //定價計畫 = 檢索定價計劃
const order = retrieveOrder(); //訂單 = 檢索訂單
let charge; //收費
const chargePerUnit = pricingPlan.unit; //每單位收費 = 定價計畫.單元

// Refactor to:

const pricingPlan = retrievePricingPlan();  //pricingPlan
const chargePerUnit = pricingPlan.unit;   //pricingPlan
const order = retrieveOrder();
let charge;
```

##### Pull Up Method (class)(ch12)
- 子類別相同.使用的方法相同直接重構.看看測試狀況。
```
class Employee {...}
class Salesman extends Employee {
 get name() {...}
}
class Engineer extends Employee {
 get name() {...}
}

// refactor to:

class Employee {
  get name() {...}
}
class Salesman extends Employee {...}
class Engineer extends Employee {...}
```
