แยกโค้ดก้อนใหญ่ๆ ให้กลายเป็นชิ้นส่วนเล็กๆ ด้วยฟังก์ชัน
ในบทก่อนหน้า เกี่ยวกับเรื่อง
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. คัดลอกโค้ดที่เกี่ยวข้องไปยังฟังก์ชันใหม่
จากนั้นเราจะคัดลอกโค้ดที่เกี่ยวข้องกับการคำนวณเกรด ไปใส่ในฟังก์ชัน
getGradejslet 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>