แยกโค้ดก้อนใหญ่ๆ ให้กลายเป็นชิ้นส่วนเล็กๆ ด้วยฟังก์ชัน
ในบทก่อนหน้า เกี่ยวกับเรื่อง
if
เราได้ลองสร้างโปรแกรมสำหรับคำนวณเกรดครับโดยโค้ดจากครั้งที่แล้ว เป็นแบบนี้:
jscalculateGrade.onclick = () => { let score = Number(scoreInput.value) let grade if (score >= 80) { grade = 'A' } else if (score >= 70) { grade = 'B' } else if (score >= 60) { grade = 'C' } else if (score >= 50) { grade = 'D' } else { grade = 'F' } alert('Your grade is ' + grade) }
จะเห็นว่า ภายในฟังก์ชันของเรา มีการดำเนินการสามขั้นตอน:
- Input:
รับคะแนนจากผู้ใช้ ผ่านกล่องข้อความ
scoreInput
(บรรทัดที่ 2) - Process: คำนวณเกรดจากคะแนนที่ได้ (บรรทัดที่ 3–14)
- Output:
แสดงเกรดที่ได้ผ่านกล่องป๊อปอัพ
alert
(บรรทัดที่ 15)
- Input:
รับคะแนนจากผู้ใช้ ผ่านกล่องข้อความ
เนื่องจากโค้ด I/O (input/output) และโค้ดที่คำนวณเกรด ถูกมัดรวมกันอยู่ในฟังก์ชันเดียว วิธีเดียวที่จะทดสอบโค้ดนี้และตรวจสอบว่ามันทำงานอย่างถูกต้องหรือไม่ ก็เราต้องกรอกเลข กดปุ่ม และตรวจสอบผลลัพธ์เอาเอง น่าจะดีกว่าถ้าเราสามารถแยกโค้ดของการคำนวณเกรดออกเป็นฟังก์ชันแยกต่างหาก เพื่อที่เราจะได้ทดสอบมันตรงๆ ได้
ขั้นตอนการรีแฟคเตอร์
เราจะรีเฟคเตอร์โค้ดนี้โดยใช้ท่า Extract Function เช่นเดิมครับ ประกอบด้วย 3 ขั้นตอนหลักๆ
- สร้างฟังก์ชันใหม่
- คัดลอกโค้ดที่เกี่ยวข้องไปยังฟังก์ชันใหม่
- แทนที่โค้ดเดิมด้วยการเรียกใช้งานฟังก์ชันใหม่
1. สร้างฟังก์ชันใหม่
เริ่มจากการสร้างฟังก์ชันใหม่ขึ้นมาก่อน ซึ่งเราจะตั้งชื่อว่า
getGrade
ฟังก์ชันนี้ควรจะรับคะแนนเข้าไปเป็นพารามิเตอร์ และส่งค่าเกรดกลับออกมาjslet getGrade = (score) => { }
2. คัดลอกโค้ดที่เกี่ยวข้องไปยังฟังก์ชันใหม่
จากนั้นเราจะคัดลอกโค้ดที่เกี่ยวข้องกับการคำนวณเกรด ไปใส่ในฟังก์ชัน
getGrade
jslet getGrade = (score) => { let grade if (score >= 80) { grade = 'A' } else if (score >= 70) { grade = 'B' } else if (score >= 60) { grade = 'C' } else if (score >= 50) { grade = 'D' } else { grade = 'F' } }
โค้ดนี้ยังไม่สมบูรณ์
โค้ดนี้ยังมีไม่สมบูรณ์ครับ แต่เพราะอะไร เราจะมาลองทดสอบกันดู
ลองเรียกใช้งานฟังก์ชัน
getGrade
ดูgetGrade(85)
undefined
จะเห็นว่า ฟังก์ชัน
getGrade
ยังไม่ส่งค่าเกรดกลับออกมา นั่นก็เพราะว่า เรายังไม่ได้ใส่คำสั่งreturn
ในฟังก์ชันgetGrade
ครับ แก้ไขโดยการเพิ่มคำสั่งreturn
เข้าไป:jslet getGrade = (score) => { let grade if (score >= 80) { grade = 'A' } else if (score >= 70) { grade = 'B' } else if (score >= 60) { grade = 'C' } else if (score >= 50) { grade = 'D' } else { grade = 'F' } return grade }
ลองเรียกใช้งานฟังก์ชัน
getGrade
ดูใหม่getGrade(85)
"A"
จะเห็นว่า ฟังก์ชัน
getGrade
สามารถส่งค่าเกรดกลับออกมาได้แล้วครับลองทดสอบกรณีอื่นๆ ดู
ลองทดสอบกรณีอื่นๆ ดูด้วย เช่น
getGrade(75)
หรือgetGrade(55)
เพื่อให้เรามั่นใจว่าฟังก์ชันgetGrade
ยังคงทำงานถูกต้องอยู่นอกจากนี้ ภายในฟังก์ชัน
getGrade
เรายังสามารถเลิกใช้ตัวแปรgrade
ได้ โดยการใช้คำสั่งreturn
ภายในผลของแต่ละเงื่อนไขได้เลย:jslet getGrade = (score) => { let grade if (score >= 80) { return 'A' grade = 'A' } else if (score >= 70) { return 'B' grade = 'B' } else if (score >= 60) { return 'C' grade = 'C' } else if (score >= 50) { return 'D' grade = 'D' } else { return 'F' grade = 'F' } return grade }
ซึ่งจะทำให้ฟังก์ชันของเราสั้นลงไปอีกครับ:
jslet getGrade = (score) => { if (score >= 80) { return 'A' } else if (score >= 70) { return 'B' } else if (score >= 60) { return 'C' } else if (score >= 50) { return 'D' } else { return 'F' } }
แก้โค้ดแล้วอย่าลืมทดสอบซ้ำ
เมื่อใดก็ตามที่เราปรับโครงสร้างหรือทำการเปลี่ยนแปลงอื่นๆ กับโค้ดที่เราเขียน เราอาจเผลอทำอะไรสักอย่างผิดพลาด ส่งผลให้โค้ดของเราพัง ดังนั้น เมื่อเราแก้โค้ด เราจึงควรทดสอบโค้ดของเราใหม่ เพื่อให้แน่ใจว่ามันยังทำงานได้ตามที่คาดหวังไว้
3. แทนที่โค้ดเดิมด้วยการเรียกใช้งานฟังก์ชันใหม่
เมื่อเรารีแฟคเตอร์โค้ดของเราแล้ว จะทำให้โค้ดใน
calculateGrade.onclick
ของเราสั้นลงมากjscalculateGrade.onclick = () => { let score = Number(scoreInput.value) let grade grade = getGrade(score) if (score >= 80) { grade = 'A' } else if (score >= 70) { grade = 'B' } else if (score >= 60) { grade = 'C' } else if (score >= 50) { grade = 'D' } else { grade = 'F' } alert('Your grade is ' + grade) }
นอกจากนี้ เราสามารถกำหนดค่าตัวแปร
grade
ตั้งแต่ตอนที่สร้างตัวแปรได้เลยjscalculateGrade.onclick = () => { let score = Number(scoreInput.value) let grade = getGrade(score) let grade grade = getGrade(score) alert('Your grade is ' + grade) }
อันที่จริง เราสามารถรีแฟคเตอร์ด้วยท่า Inline Variable เพื่อทำให้โค้ดของเราเหลือบรรทัดเดียวได้เลย
jscalculateGrade.onclick = () => { let score = Number(scoreInput.value) let grade = getGrade(score) alert('Your grade is ' + grade) alert('Your grade is ' + getGrade(Number(scoreInput.value))) }
แต่ผมคิดว่ามันทำให้โค้ดนี้อ่านยากกว่าเดิม ดังนั้นผมจึงกลับไปใช้โค้ด 3 บรรทัดแบบเดิม ผมชอบโค้ดแบบนี้มากกว่าเพราะว่ามันแยกชัดเจนระหว่าง Input/Process/Output:
jscalculateGrade.onclick = () => { let score = Number(scoreInput.value) // Input let grade = getGrade(score) // Process alert('Your grade is ' + grade) // Output }
และนี่คือโค้ดที่ผ่านการรีแฟคเตอร์แล้วครับ
ดูโค้ด
html<!doctype html> <html> <head> <title>Grade Calculator</title> </head> <body> Your score: <input id="scoreInput" value="100" type="number" max="100" min="0"> <input id="calculateGrade" value="Calculate grade" type="button"> <script> let scoreInput = document.getElementById('scoreInput') let calculateGrade = document.getElementById('calculateGrade') let getGrade = (score) => { if (score >= 80) { return 'A' } else if (score >= 70) { return 'B' } else if (score >= 60) { return 'C' } else if (score >= 50) { return 'D' } else { return 'F' } } calculateGrade.onclick = () => { let score = Number(scoreInput.value) let grade = getGrade(score) alert('Your grade is ' + grade) } </script> </body> </html>