[TOC]
## Code review #1 by 阿傑
### Q11
* 我的getAnswers function為了符合兩種情況(直接呼叫或是賦值給變數),在import時使用了as作為別名,阿傑建議再取一個更抽象的名字同時符合兩種情況,少用as
* 但我實在想不到要怎麼取名,先跳過orz
```javascript
import { getAnswers as askQuestions } from "../utilities/getAnswers.js";
// 為了符合兩種情況
// 第一種是直接呼叫
askQuestions()
// 第二種是賦值給變數
const answer = getAnswers()
```
* q11.js已經像是namespace,function命名就不需要Q11了
```javascript
// 原本寫的
import { validationsInQ11, messageOfQ11 } from "./q11.js";
// 改成
import { validations, message } from "./q11.js";
```
* 驗證器只會跟輸入有關,所以接口統一是拿來接字串的
* 如果接口不統一表示可能不只拿來驗證
* 但這跟Chris說的不一樣QQ
```javascript
// 原本寫的
// 依照驗證器要驗證的型別來決定接收參數的型別
export function validationsInQ11(strInput) {
isEmpty(strInput);
const numInput = Number(strInput);
isIntegerNumber(numInput);
isPositiveNumber(numInput);
}
// 阿傑的範例code
// 如果接口參數不統一,會認為是有其他功能,而不是只有驗證
// 例如isPositive還可以用在其他地方
function isPositive(num) {
return num > 0
}
// 其中一個地方是驗證器(isPositiveNumber)
function isPositiveNumber(num) {
if (isPositive(+num)) {
throw new Error()
}
}
```
* 傾向分別測試裡面用到的function,而不是只測一個大的function
* 例如下面這個範例,阿傑認為應該針對validationsInQ11、getTotalFee測試
* 這跟Chris講的也不一樣QQ
```javascript
// 範例
export function messageOfQ11(strPeriod) {
validationsInQ11(strPeriod);
const period = Number(strPeriod);
return `${period}期的總費用為${getTotalFee(period)}元`;
}
```
### Q12
* 參數命名為range,會覺得是一個陣列或是兩個數字,但我設計其實是單一的數字
* 可以把可語意化或可重複使用的東西拆成function放到common.js集中起來
```javascript
// 原本寫的
function getPrimeNumbers(start, end) {
const length = end - start + 1;
return Array.from(Array(length), (x, index) => index + start).filter(isPrime);
// 可以改成
// 把getArray拆出來作為common.js的一部分
function getArray(start, end) {
const length = end - start + 1;
return Array.from(Array(length), (x, index) => index + start)
}
function getPrimeNumbers(start, end) {
return getArray(start, end).filter(isPrime)
}
```
### Q13
* main()看不出解題流程
* 但這樣不確定要怎麼連同輸出文字一起測試
```javascript
// 原本寫的,流程都包在messageOfQ13裡面
function main() {
const goalDistance = 1000;
console.log(messageOfQ13(goalDistance));
}
// 建議改成
function main() {
const goalDistance = 1000;
const rabbitTime = getRabbitRestTime(goalDistance)
const turtleTime = getTurtleTime(goalDistance)
const rabbitRestTime = getRabbitRestTime(turtleTime, rabbitTime)
const result = getMessageOfQ13(rabbitRestTime)
console.log(result);
}
```
* pure function
1. 不要改動到外部變數
2. 盡量不要在function內部呼叫其他function,通常只有主程式main會呼叫其他function
### getAnswers.js
* 我的promise只用到resolve沒有用到reject
* 沒有用到reject對promise來說是很奇怪的一件事
* 應該要可以自己處理reject後的事情,不是讓function處理掉,不然就沒有使用promise的意義在了
## Code review #2 by Angela
### Q15
* Angela習慣上會把先遇到的function放檔案最前面,反正函式會提升
* 一樣會習慣在main看到解題流程,但確實這樣測試時就會只測小的function
## Code review #3 by Eva
### getAnswers
* 一個function做了太多事情了(問問題、得到使用者輸入、驗證、得到答案),最好可以拆分這些事情
* 目前設計一次可以傳入問題陣列,依照陣列長度得到多個答案,可以把一個問題的情況跟多個問題的情況拆開
### Q14
* 轉型別的時機
```javascript
// 原本寫的
async function main() {
const firstAnswers = await getAnswers(
["請輸入初始細菌數量: "],
validationsForBacteria
);
const strOriginalBacteria = firstAnswers[0];
const secondAnswers = await getAnswers(
["請輸入分鐘數: "],
validationsForMinute
);
const strMinutes = secondAnswers[0];
// 原本在messageOfQ14()裡面才轉型
console.log(messageOfQ14(+strOriginalBacteria, strMinutes));
}
// 可以改成
async function main() {
const firstAnswers = await getAnswers(
["請輸入初始細菌數量: "],
validationsForBacteria
);
const secondAnswers = await getAnswers(
["請輸入分鐘數: "],
validationsForMinute
);
// 在這邊轉型
const OriginalBacteria = Number(firstAnswers[0]);
const Minutes = Number(secondAnswers[0]);
console.log(messageOfQ14(OriginalBacteria, Minutes));
}
```
* 雖然題目twofold(b, m),但可以想像成題目是業主先隨便寫的pseudo code,所以真正的命名還是要以好讀為主
```javascript
function twofold(b, m) {}
// 改成
function twofold(bacteria, minutes) {}
```
* 測介面(輸出)只要測一次就好,其他測試可以專注在function的功能有沒有問題
* 3A原則的註解會留在上面
* act可以依照function的功能去命名
```
// Arrange
// Act
// Assert
```
## Code review #final by Chris
### Q14
* 首先問自己,是怎麼理解這個題目的?沒有對錯,依照自己理解的邏輯去寫程式就好
* 過20分鐘後,瞬間變成兩倍的細菌
* 每20分鐘觀察一次細菌的數量,發現跟上一次觀察比較,變成兩倍的細菌
* 命名
```javascript
// 原本寫的
const generationTime = 20;
// 改成
const minutesOfGenerationTime = 20;
// 這樣就可以看到分鐘數除以分鐘數
const exponent = minutes / minutesOfGenerationTime;
```
```javascript
// 原本寫的
// 不知道2是什麼意思
const totalBacteria = 2 * originalBacteria;
// 改成
// double直接代表2的意思,就可以不用解釋2了
const doubleBacteria = 2 * originalBacteria;
```
* 寫了isMultiple這個驗證
* 原本是覺得如果經過的時間不是20的倍數就不應該輸入
* 但正常來說應該要接受所有時間
### 問題
1. main要看出這支程式的解題流程,但因為要測試所以把流程都包在message裡面,那我把message放在main旁邊可以嗎?
* 因為主程式要執行`main()`,所以要測試的函式一定要放在另一個檔案裡面
2. 為什麼可以只測最大的function?
* 測試是為了之後重構的時候,確定有沒有改對,不然就會一行code都不敢動
* 如果測小的function,如果改動內容,例如把小function直接拆出來擺在大function,即使最後大function輸出結果是一樣的,可能還是要動到小function的測試
* 要避免又改code又改測試。不要去動測試!
* 只測最大的function的方法稱作社交型單元測試
* 測試就是一種會執行的spec
3. 為什麼要測輸出的文字?
* 輸出的文字很重要,測試是確保印出的訊息是正確的
* 使用者是靠螢幕上的文字來判斷這個程式是不是還是正常運作
5. import as什麼時候會用到?
* 引入第三方套件,沒辦法改套件的名字,所以只能用as來取別名
* 所以我把getAnswers as askQuestion是可以的
6. 把驗證器理解成因為有輸入所以才要驗證,所以預設接收的皆為字串?
* 可以這樣理解,但Chris不會這樣做
* Chris認為驗證容錯率要越低越好,才能偵測到garbage
7. readline promise的reject真的需要嗎?
* 因為我的promise已經設計成只要輸入錯誤就會重新問,所以在catch的地方就不需要用reject去接了
* 為了避免其他人在catch區塊找不到reject,可以在catch區塊寫註解,說明沒有reject的原因
```javascript
try {
validations(answer);
answers[indexOfQuestion] = answer;
getAnswers(indexOfQuestion + 1);
} catch (error) {
console.log(error.message);
// 在此不用reject的原因:會再呼叫一次自己,問到對為止
getAnswers(indexOfQuestion);
}
```