## 重構 第九章
---
<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}]"}