[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); } ```