## 重構 第九章 --- <div style="font-size: 30px; text-align: left; padding: 0 100px"> <p>9-1 拆開變數 Split Variable</p> <p>9-2 更改欄位名稱 Rename Field</p> <p>9-3 將衍生變數換成查詢函式 Replace Derived Variable with Query</p> </div> --- ### 9-1 #### 拆開變數 Split Variable ---- #### 範例一 #### 重構前問題 ``` javascript [-|3,10] function distanceTravelled(scenario, time){ let result; let acc = scenario.primaryForce / scenario.mass; let primaryTime = Math.min(time, scenario.delay); result = 0.5 * acc * primaryTime * primaryTime; let secondaryTime = time - scenario.delay; if(secondaryTime > 0) { let primaryVelocity = acc * scenario.delay; acc = (scenario.primaryForce + scenario.secondaryForce) / scenario.mass; result += primaryVelocity * secondaryTime + 0.5 * acc * secondaryTime * secondaryTime; } return result; } ``` <ol style="font-size: 24px;"> <li> acc 變數名稱難以識別 </li> <li> 同一個變數被重複賦值多次 <span style="font-size: 20px;">(動機 Page.280)</span> </li> </ol> ---- #### 作法 ##### Page.281 ``` javascript [3,4|6,7,11,12|13,14|15,16] function distanceTravelled(scenario, time){ let result; // let acc = scenario.primaryForce / scenario.mass; const primaryAcceleration = scenario.primaryForce / scenario.mass; let primaryTime = Math.min(time, scenario.delay); // result = 0.5 * acc * primaryTime * primaryTime; result = 0.5 * primaryAcceleration * primaryTime * primaryTime; let secondaryTime = time - scenario.delay; if(secondaryTime > 0) { // let primaryVelocity = acc * scenario.delay; let primaryVelocity = primaryAcceleration * scenario.delay; // acc = (scenario.primaryForce + scenario.secondaryForce) / scenario.mass; const secondaryAcceleration = (scenario.primaryForce + scenario.secondaryForce) / scenario.mass; // result += primaryVelocity * secondaryTime + 0.5 * acc * secondaryTime * secondaryTime; result += primaryVelocity * secondaryTime + 0.5 * secondaryAcceleration * secondaryTime * secondaryTime; } return result; } ``` <ol style="font-size: 24px;"> <li>修改變數名稱,並調整為宣告常數 const,目的為確保指被賦值一次</li> <li>修改直到下一次賦值之前的引用</li> <li>在第二次賦值的地方進行宣告常數,並修改名稱</li> <li>修改引用常數的名稱</li> </ol> ---- #### 注意 ##### Page.281 ``` javascript [2,5,11] function distanceTravelled(scenario, time){ let result; const primaryAcceleration = scenario.primaryForce / scenario.mass; let primaryTime = Math.min(time, scenario.delay); result = 0.5 * primaryAcceleration * primaryTime * primaryTime; let secondaryTime = time - scenario.delay; if(secondaryTime > 0) { let primaryVelocity = primaryAcceleration * scenario.delay; const secondaryAcceleration = (scenario.primaryForce + scenario.secondaryForce) / scenario.mass; result += primaryVelocity * secondaryTime + 0.5 * secondaryAcceleration * secondaryTime * secondaryTime; } return result; } ``` <p style="font-size: 24px;">如果賦值式連續、累加的,就不需要拆開他</p> ---- #### 範例二 #### 重構前問題 ``` javascript [] function discount(inputValue, quantity){ if(inputValue > 50) inputValue = inputValue - 2; if(quantity > 100) inputValue = inputValue - 1; return inputValue; } ``` <p style="font-size: 24px;"> 參數在 function中被重新賦予值 <span style="font-size: 20px;">(動機 Page.283)</span> </p> <p style="font-size: 20px;"> 假如參數是一個物件或是陣列,修改參數可能會導致原始資料有意外的行為。 </p> ---- #### 作法 ##### Page.281 ``` javascript [1,2,3|4,5|10-15] // function discount(inputValue, quantity){ function discount(originalInputValue, quantity){ let inputValue = originalInputValue; // if(inputValue > 50) inputValue = inputValue - 2; if(originalInputValue > 50) inputValue = inputValue - 2; if(quantity > 100) inputValue = inputValue - 1; return inputValue; } function discount(originalInputValue, quantity){ let result = originalInputValue; if(originalInputValue > 50) result = result - 2; if(quantity > 100) result = result - 1; return result; } ``` <ol style="font-size: 24px;"> <li>將輸入參數與變數拆開</li> <li>將引用變數的地方改為輸入參數</li> <li>修改變數名稱</li> </ol> ---- #### 9-1 結論 <p style="font-size: 24px; padding: 0 30px"> 變數都應該只被設定過一次,如果被多次賦予值,表示在 function 內的責任超過一次,此時就應該執行拆開變數。 </p> --- ### 9-2 #### 更改欄位名稱 Rename Field ---- #### 情境一 ```javascript= function user() { const information = { // nm: "Lucy", name: "Lucy", country: "GB" } // ... use information.name ... } ``` <p style="font-size: 24px; padding: 0 30px"> 在區域範圍內使用,看不懂欄位名稱,就是直接修改 </p> ---- #### 情境二 ``` javascript [1-4|6-20|22-30|34-35|39-40|52-53|63-64|68-71] const organization = { name: "Acme Gooseberries", country: "GB" } // 1. class Organiztion { #name; #country; 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 } } // 2. // const organization = { // name: "Acme Gooseberries", // country: "GB" // } const organization = new Organiztion({ name: "Acme Gooseberries", country: "GB" }); // 3. & 4. class Organiztion { // #name; #title; #country; constructor(data) { // 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 } } // 5. const organization = new Organiztion({ // name: "Acme Gooseberries", title: "Acme Gooseberries", country: "GB" }); // 6. & 7. class Organiztion { #title; #country; constructor(data) { // 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 title() { return this.#title }; set title(aString) { this.#title = aString }; get country() { return this.#country }; set country(aCountryCode) { this.#country = aCountryCode }; } ``` <ol style="font-size: 24px;"> <li> 如果物件的使用範圍廣泛,會先進行封裝,<br/>封裝<span style="font-size:20px">(P.188)</span>是因為可以用小步驟來進行修改,而非一次性完成 </li> <li>封裝後將常數物件改用建構式</li> <li>更改私有欄位名稱</li> <li>改用三元運算判斷,讓建構式可以同時使用新舊名稱</li> <li>將呼叫建構式的地方調整成改過的名稱</li> <li>當呼叫的名稱都改完了,就移除判斷改用新名稱</li> <li>調整 get & set 名稱</li> </ol> --- ### 9-3 #### 衍伸變數換成查詢函示 Replace Derived Variable with Query ---- #### 範例 #### 重構前問題 ``` javascript // ... class productionPlan constructor() { this._production = 0; this._adjustments = []; } get production() { return this._production }; applyAdjustment(anAdjustment) { this._adjustments.push(anAdjustment); this._production += anAdjustment.amount; } ``` <ol style="font-size: 24px;"> <li> 資料的重複,_production 的資料來源其實就是 _adjustments </li> </ol> ---- #### 情境一,無設定初始值 ##### Page.291 ``` javascript [2,5-8|15-24|27] get production() { console.assert(this._production === this.calculatedProduction); return this._production; }; get calculatedProduction() { return this._adjustments .reduce((sum, adjustment) => sum + adjustment.amount, 0); } applyAdjustment(anAdjustment) { this._adjustments.push(anAdjustment); this._production += anAdjustment.amount; } // 2. // get production() { // console.assert(this._production === this.calculatedProduction); // return this._production; // }; // get calculatedProduction() { get production() { return this._adjustments .reduce((sum, adjustment) => sum + adjustment.amount, 0); } applyAdjustment(anAdjustment) { this._adjustments.push(anAdjustment); // this._production += anAdjustment.amount; } ``` <ol style="font-size: 24px; padding: 0 100px"> <li>先建立一個函式來計算變數,並使用斷言的方式來確認新的函示與原本的變數是否相同</li> <li>進行測試,沒有錯就將計算函式直接搬到原本的變數中</li> <li>移除多餘的程式碼</li> </ol> ---- #### 情境二,有初始值 ##### Page.292 ``` javascript [1-9|13-15,19-20,24-25|34-41|32,45] constructor(production) { this._production = production; this.adjustments = []; } get production() { return this._production }; applyAdjustment(anAdjustment) { this._adjustments.push(anAdjustment); this._production += anAdjustment.amount; } // 1. constructor(production) { // this._production = production; this._initialProduction = production; this._productionAccumulator = 0; this.adjustments = []; } get production() { // return this._production; return this._initialProduction + this._productionAccumulator; }; applyAdjustment(anAdjustment) { this._adjustments.push(anAdjustment); // this._production += anAdjustment.amount; this._productionAccumulator += anAdjustment.amount; } // 2. constructor(production) { this._initialProduction = production; // this._productionAccumulator = 0; this.adjustments = []; } get production() { // return this._initialProduction + this._productionAccumulator; return this._initialProduction + this.calculatedProductionAccumulator; }; get calculatedProductionAccumulator() { return this._adjustments .reduce((sum, adjustment) => sum + adjustment, 0); } applyAdjustment(anAdjustment) { this._adjustments.push(anAdjustment); // this._productionAccumulator += anAdjustment.amount; } ``` <ol style="font-size: 24px; padding: 0 100px"> <li>拆開變數,避免一個變數有多個職責</li> <li>中間的過程與剛剛一樣,建立一個計算函示並斷言是否與原本變數相同,相同就將變數替換成計算函示</li> <li>把多餘的程式碼刪掉</li> </ol> ---- #### 其他常見範例 <div style="width: 100%; height: 0; padding-bottom: 56.25%; position: relative; "> <iframe width="100%" height="100%" style="position:absolute; left: 0; left: 50%;transform: translateX(-50%);" src="https://www.youtube.com/embed/GGo3MVBFr1A?start=511" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> </div>
{"metaMigratedAt":"2023-06-17T17:26:45.800Z","metaMigratedFrom":"YAML","breaks":true,"title":"重構 第九章","contributors":"[{\"id\":\"2730d44b-3f0a-4f3e-b481-6c66d5239962\",\"add\":15864,\"del\":5566}]"}
    119 views