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

##### 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://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 {...}
```