5 วิธีที่ทำให้ Code Clean ขึ้นแบบง่ายๆ

Pawut Jingjit
3 min readMay 4, 2023

เมื่อทำ Production จริง เพื่อนๆน่าจะมีประสบการณ์ที่ยิ่งเขียน Code จะยิ่งอ่านยากไปเรื่อยๆ แน่นอนว่า Code ที่ไม่ “Clean” นั้น ก็คงไม่สร้างปัญหากับเพื่อนๆในทันที

แต่ในระยะยาว เมื่อเพื่อนต้องกลับมาอ่าน Code อีกครั้ง “Code ที่ไม่ Clean” นั่นละ จะทำให้เพื่อนๆเริ่มเสียเวลา รวมไปถึงเพื่อนร่วมทีมคนใหม่ ที่ต้องมานั่งแกะ Legacy Code ด้วย

Call Back Hell น่าจะเป็นตัวอย่างที่ดีของ Code ที่ไม่คลีน (ภาพสวยๆจาก Nikhil Upadhyay)

ได้เวลาแล้วหรือยัง ที่เพื่อนๆจะเริ่มต้น Refactor Code ของเพื่อนๆ

Guard Clauses

ตั้งแต่เราเรียนมหาลัย เขาก็แนะนำให้ใช้ If Else Statement กัน ซึ่งถ้าใช้เพียง 1 Nested Loop ก็คงไม่เป็นปัญหาอะไร

//เริ่มต้นด้วยปัญหา Classic จะสังเกตว่า แค่ 2 Nested Loop ก็เริ่มเข้าใจยากแล้ว 
// ซึ่ง Business Logic ปกติมักจะซับซ้อนกว่านี้
function userIsAdmin(user) {
if (user.role == 'admin') {
// ------------- 1 ----------------------
if (user.manager == true) {
return true;
}
else {
return false;
}
// ------------------------------------
}
else {
// ------------ 2 ----------------------
return false;
// ------------------------------------
}
}
// วิธีปลดที่ง่ายที่สุดคือถ้าเจอ If , Else ให้สลับ Logic โดยใส่นิเสธใน IF จะปลด else ออกได้เสมอ
function userIsAdmin(user) {
if (user.role != 'admin') {
// ------------ 2 ----------------------
return false;
// ------------------------------------
}
if (user.manager == true) {
// ------------- 1 ----------------------
if (user.manager == true) {
return true;
}
else {
return false;
}
// ------------------------------------
}
// ใน 1 ยังเหลือ if-else อยู่ จึงทำขั้นตอนเดิม
function userIsAdmin(user) {
if (user.role != 'admin') {
// ------------ 2 ----------------------
return false;
// ------------------------------------
}
if (user.manager != true) {
// ------------- 1 ----------------------
return false;
}
return true
// ------------------------------------
}
// เมื่อเอา Comment และ {} ออกหมด(เพราะเหลือเพียงคำสั่งเดียว)
// สังเกตว่า จากโค๊ตที่อ่านยาก จะ Clean ขึ้นอย่างชัดเจน
function userIsAdmin(user) {
if (user.role != 'admin') return false
if (user.manager != true) return false;
return true
}

Remove Control Flag

เพื่อนๆที่เรียน Programming จากมหาลัย อาจจะติดการใช้ Control-Flag มา อาจจะเพื่อควบคุมการทำงานใน For Loop แต่ตัว Control-Flag เอง จะทำให้ Code เราซับซ้อนขึ้นอย่างไม่จำเป็น

// ถ้ามี String ใน List(String) ให้ Log Found 
// ถ้าไม่พบเลย ให้ Log Not Found (ห้าม Log เกิน 1ที)
function findStringInList(s,list){
let controlFlag = false
for(let i=0 ;i<list.length;i++){
if(controlFlag == false)
if(list[i] === s)
controlFlag = true
console.log("Found")
}
if(controlFlag === false)
console.log("Not Found")
}
// ใช้ return แทน เพื่อจบ Function
// ใช้ break แทน เพื่อจบ Loop
// ใช้ continue แทน เพื่อให้ Loop ไปสู่ Loop ต่อไป
function findStringInList(s, list) {
for (let i = 0; i < list.length; i++) {
if (list[i] === s) {
console.log("Found");
return;
}
}
console.log("Not Found");
}

Remove Temporary Variable

ตัวแปร Temporary Variable คือตัวแปรที่ถูกใช้งานเพียงแค่เวลาสั้นๆ ซึ่งถ้ามีเยอะไป จะทำให้ Code เราซับซ้อนขึ้น

โดยเฉพาะถ้าเยอะเสียจนเราตั้งชื่อมันว่า Temp แล้ว ตอนนี้ Code เราจะไม่ Clean อีกต่อไป

function filterMoreThen5(arr) {
const filterArray = arr.filter(x => x >= 5)
return filterArray
}
// สังเกตว่า filterArray นั้นจะไม่มีหน้าที่อะไรเลย นอกจากเก็บตัวแปรไว้ตัวหนึ่งก่อนจะ return เท่านั้น

function filterMoreThen5(arr) {
return arr.filter(x => x >= 5)
}
// เราสามารถ return ได้เลย โดยที่ไม่ต้องสร้างตัวแปรตัวใหม่เพิ่ม

Don’t pass many Param to Function

Function นั้นไม่ควรจะรับ Parameter มากเกินไป ถ้าเลี่ยงไม่ได้จริงๆควรจะห่อ Object ก่อน Pass เข้าไปใน Function

function createUser(userName, password, email, tel, address) {
//Create User
}

function createUser(User) {
const { userName, password, email, tel, address } = User
//Create User
}

// Function แรก การจะเรียกใช้แต่ละที ต้อง Pass ไปถึง 5 Param โดยต้อง Order ตรง
// (ยังไม่นับว่ามีโอกาสที่บาง Param เป็น Null) กลับกัน Function ที่ 2 Pass แค่ Object เพียงตัวเดียว
// นอกจากจะ Clean กว่าแล้ว ยัง Meaning กว่าด้วย

อย่างไรก็ตาม การห่อด้วย Object นั้น ยากแก่การสร้าง Object อยู่ดี

แทนที่จะ Pass 5 Param เข้า Function เราจะ Pass 5 Param เข้าสู่ Constructor ของ Object อยู่ดี)

ควรใช้ร่วมกับ Design Pattern อย่าง Builder จะลดปัญหาเช่นนี้ได้

Extract Function

ถ้า Code นั้นมีความซับซ้อนเกินไป เราควร Extract Logic ที่ซับซ้อนนั้น ออกเป็น Function ที่ “ความรับผิดชอบ” ลดลงไป

function signIn(userName, password) {
if (userName.length < 6 || userName.length > 13) {
console.log("Username is invalid format")
return
}
//Sign In Method
}

const isUserNameIsValid = userName => userName.length < 6 || userName.length > 13
function signIn(userName, password) {
if (isUserNameIsValid(userName)) {
console.log("Username is invalid format")
return
}
//Sign In Method
}

// สังเกตได้ว่า เราอาจจะไม่เข้าใจ userName.length < 6 || userName.length > 13 นั้นหมายถึงอะไร
// แต่เมื่อ Extract ถึงเราจะไม่เข้าใจหลักการทำงาน เราก็ยังทราบว่า ใช้เช็ค Username ว่า Valid ไหม

ข้อดีอีกอย่างของการ Extract Function คือ Function ที่เรา Extract ออกมานั้นจะง่ายต่อการ Test อีกด้วย

บทส่งท้าย

ยังมีอีกหลายเทคนิค สำหรับการ Clean Code ที่ไม่อยู่ในบทความนี้ (ในที่นี้ คัดมาแต่ที่น่าจะได้ใช้บ่อยๆ)

ต้องสารภาพว่า “Code ไม่ Clean” (หรือ Code Small) เกิดได้หลายสาเหตุมาก การใช้วิธีในบทความนี้แก้ปัญหา จะแก้ได้เพียง Style Code เท่านั้น

ปัญหาส่วนใหญ่ที่เจ้าของบทความพบ มักจะเป็น Code Small ระดับ Structure เลย (อาจเกิดจากไม่ได้ใช้ Pattern Design หรือปัญหาล้านแปดอื่นๆ)

อย่างไรก็ตาม ถ้าวันหนึ่งเพื่อนๆตรวจ Commit Code ของเด็กฝึกงาน แล้วสามารถชี้ได้ว่า “Line นี้ ควรจะใช้ Guard Clauses นะ” ผมก็คิดว่า การอ่านบทความนี้นั้นมีความหมายแล้ว LOL

ที่ต้องเตือนอย่างยิ่งคือ อย่าพยายาม Refactor Code ที่ไม่มี Test เนื่องจากเราจะไม่รู้เลย หลังจาก Refactor มาแล้ว Code จะทำงานได้เหมือนเดิมหรือไม่ (จริงๆควรเขียน Test เสมอ แต่บางทีมอาจมีข้อจำกัดด้านเวลาจริงๆ)

ท้ายที่สุด ก็หวังว่าบทความนี้จะมีประโยชน์กับเพื่อนๆนะครับ พบกันใหม่ในโอกาสต่อๆไป

Reference

--

--