Clean Code: ฟังก์ชันควรทำอะไร?
จากบทที่แล้วที่เราได้พูดคุยกันเรื่องความสำคัญของการตั้งชื่อที่ดีใน Clean Code วันนี้เราจะมาเจาะลึกกันต่อในเรื่องของฟังก์ชัน หนึ่งในองค์ประกอบพื้นฐานที่สุดของการเขียนโปรแกรม ฟังก์ชันที่ดีนั้น นอกจากจะมีชื่อที่สื่อความหมายแล้ว ยังต้องมีคุณสมบัติอื่นๆ ที่จะช่วยให้โค้ดของเราสะอาด อ่านง่าย และบำรุงรักษาง่ายอีกด้วย
Single Responsibility Principle: หนึ่งฟังก์ชัน หนึ่งหน้าที่
หัวใจสำคัญของการเขียนฟังก์ชันที่ดีคือ Single Responsibility Principle (SRP) หรือหลักการความรับผิดชอบเดียว ซึ่งหมายความว่า แต่ละฟังก์ชันควรมีหน้าที่เดียวเท่านั้น
ลองจินตนาการว่าคุณมีเครื่องมืออเนกประสงค์ที่ทำได้ทุกอย่าง ตั้งแต่ปอกผลไม้ ยันซ่อมรถยนต์ มันคงจะใช้งานยากและประสิทธิภาพไม่ดีเท่าเครื่องมือเฉพาะทางใช่ไหมครับ? ฟังก์ชันก็เช่นกัน ถ้าฟังก์ชันหนึ่งทำหลายอย่างเกินไป จะทำให้โค้ดซับซ้อน เข้าใจยาก และแก้ไขยาก
ตัวอย่างที่ไม่ดี:
function processOrder($orderId) {
// 1. ดึงข้อมูล Order จาก Database
$order = getOrderFromDatabase($orderId);
// 2. คำนวณราคารวม
$total = calculateTotal($order);
// 3. ส่งอีเมลยืนยันการสั่งซื้อ
sendConfirmationEmail($order['email'], $total);
// 4. อัพเดทสถานะ Order ใน Database
updateOrderStatus($orderId, 'confirmed');
}
ฟังก์ชัน processOrder นี้ทำถึง 4 อย่าง ซึ่งเป็นการละเมิดหลักการ SRP
ตัวอย่างที่ดี:
function processOrder($orderId) {
$order = getOrder($orderId);
$total = calculateOrderTotal($order);
confirmOrder($order, $total);
}
function getOrder($orderId) {
return getOrderFromDatabase($orderId);
}
function calculateOrderTotal($order) {
return calculateTotal($order);
}
function confirmOrder($order, $total) {
sendConfirmationEmail($order['email'], $total);
updateOrderStatus($order['id'], 'confirmed');
}
เราได้แยกฟังก์ชัน processOrder ออกเป็นฟังก์ชันย่อยๆ ที่มีหน้าที่เดียว ทำให้โค้ดอ่านง่ายขึ้น และถ้าต้องการแก้ไขส่วนใดส่วนหนึ่ง ก็สามารถทำได้โดยไม่กระทบส่วนอื่น
ฟังก์ชันขนาดเล็ก: ยิ่งเล็ก ยิ่งดี
โดยทั่วไปแล้ว ฟังก์ชันที่ดีควรมีขนาดเล็ก โดยไม่ควรเกิน 20-30 บรรทัด หากฟังก์ชันของคุณยาวกว่านี้ ลองพิจารณาแยกฟังก์ชันออกเป็นฟังก์ชันย่อยๆ ที่มีหน้าที่เดียว เหมือนที่เราทำในตัวอย่างก่อนหน้านี้
ทำไมฟังก์ชันขนาดเล็กถึงดี?
- อ่านง่าย: ฟังก์ชันขนาดเล็กจะเข้าใจง่ายกว่า เพราะมีโค้ดน้อยกว่า
- ทดสอบง่าย: ฟังก์ชันขนาดเล็กจะทดสอบง่ายกว่า เพราะมีกรณีทดสอบน้อยกว่า
- นำกลับมาใช้ใหม่ได้ง่าย: ฟังก์ชันขนาดเล็กที่มีหน้าที่เดียว มักจะสามารถนำกลับมาใช้ใหม่ในส่วนอื่นๆ ของโปรแกรมได้
- แก้ไขง่าย: ฟังก์ชันขนาดเล็กจะแก้ไขง่ายกว่า เพราะมีการเปลี่ยนแปลงที่กระทบกับส่วนอื่นๆ น้อยกว่า
แน่นอนว่าการกำหนดขนาดที่ตายตัวอาจไม่ใช่เรื่องที่ถูกต้องเสมอไป แต่โดยทั่วไปแล้ว ถ้าคุณรู้สึกว่าฟังก์ชันของคุณเริ่มยาวและซับซ้อนเกินไป นั่นอาจเป็นสัญญาณว่าถึงเวลาที่ต้อง Refactor แล้ว
หลีกเลี่ยง Side Effects: ฟังก์ชันควรทำตามที่บอกเท่านั้น
Side Effects คือการที่ฟังก์ชันเปลี่ยนแปลงสถานะภายนอก (เช่น ตัวแปร Global, Database, File) นอกเหนือจากค่าที่ฟังก์ชันส่งกลับมา
Side Effects เป็นสิ่งที่ควรหลีกเลี่ยง เพราะทำให้โค้ดคาดเดาได้ยาก และทำให้เกิด Bug ได้ง่าย
ตัวอย่าง:
$discount = 0.1; // ตัวแปร Global
function applyDiscount($price) {
global $discount;
$price = $price * (1 - $discount);
$discount = 0.2; // เปลี่ยนค่าตัวแปร Global
return $price;
}
$originalPrice = 100;
$discountedPrice = applyDiscount($originalPrice);
echo "Original Price: " . $originalPrice . "\n";
echo "Discounted Price: " . $discountedPrice . "\n";
echo "Discount: " . $discount . "\n";
ในตัวอย่างนี้ ฟังก์ชัน applyDiscount ไม่เพียงแต่คำนวณราคาที่ลดแล้วเท่านั้น แต่ยังเปลี่ยนแปลงค่าของตัวแปร Global $discount อีกด้วย ทำให้ผลลัพธ์ของโปรแกรมคาดเดาได้ยาก
วิธีแก้ไข:
function applyDiscount($price, $discount) {
return $price * (1 - $discount);
}
$discount = 0.1;
$originalPrice = 100;
$discountedPrice = applyDiscount($originalPrice, $discount);
echo "Original Price: " . $originalPrice . "\n";
echo "Discounted Price: " . $discountedPrice . "\n";
echo "Discount: " . $discount . "\n";
เราได้แก้ไขฟังก์ชัน applyDiscount ให้รับค่า $discount เป็น Parameter แทนที่จะใช้ตัวแปร Global ทำให้ฟังก์ชันมีความเป็นอิสระมากขึ้น และไม่มี Side Effects
หลักการง่ายๆ ในการหลีกเลี่ยง Side Effects:
- ฟังก์ชันควรรับข้อมูลที่ต้องการจาก Parameter เท่านั้น
- ฟังก์ชันควรส่งกลับค่าที่คำนวณได้เท่านั้น
- ฟังก์ชันไม่ควรเปลี่ยนแปลงตัวแปร Global หรือสถานะภายนอกอื่นๆ
สรุป
การเขียนฟังก์ชันที่ดีเป็นสิ่งสำคัญในการสร้าง Clean Code ที่อ่านง่าย บำรุงรักษาง่าย และลดความเสี่ยงในการเกิด Bug โดยสรุปแล้ว ฟังก์ชันที่ดีควรมีคุณสมบัติดังนี้:
- Single Responsibility Principle: ทำหน้าที่เดียวเท่านั้น
- ขนาดเล็ก: ไม่ยาวจนเกินไป
- ไม่มี Side Effects: ไม่เปลี่ยนแปลงสถานะภายนอก
ในบทต่อไป เราจะมาพูดถึงเรื่อง Comments และวิธีเขียน Comments ที่ดี (และ Comments ที่ไม่ดี) เพื่อให้โค้ดของเราเข้าใจง่ายยิ่งขึ้น อย่าลืมติดตามกันนะครับ!