“ตอกตะปูน” และ “ทำฮาร์ดโค้ด” เป็นคำศัพท์เวงที่หมายถึงการกำหนดค่าแน่นอนในโค้ดโปรแกรมโดยตรง แทนที่จะนำไปไว้ในการตั้งค่าหรือคอนฟิกุรัชัน Hardcode เป็นหนึ่งในแอนตีพะเทิร์นที่รู้จักดีที่สุดในการพัฒนา เพราะมันลดความยืดหยุ่นและความสามารถใช้ซ้ำของโค้ด ตามข้อมูลจาก Refactoring Guru แฮร์ดโค้ดทำให้การทดสอบ การบำรุง และการปรับแอปพลิเคชันให้เข้ากับสภาพแวดล้อมแตกต่างๆได้ยากขึ้น การใช้ค่าคงอย่างมีสติแทน hardcode เป็นสัญญาณของสถาปัตยกรรมที่โตเต็มโตเต็ม
หัวข้อสำคัญ
ทำฮาร์ดโค้ด (ตอกตะปูน) — สอดค่าเฉพาะเจาะจงในโค้ดโปรแกรมโดยตรงเสียนนานที่การเปลี่ยนแปลงต้องแก้ไขโค้ดต้นฉบับและคอมไพล์แอปพลิเคชันใหม่ การเปรียบเทียมแบบ“ตอกตะปูน”สะท้อนถึงสาระสำคัญได้อย่างถูกต้อง: ค่าถูกกำหนดไว้อย่างถาวร และสามารถแยกออกจากโค้ดได้โดยต้องใช้ความพยายาม
ตัวอย่างของ hardcode — URL เซิร์ฟเวอร์ที่เขียนเป็นคำสั่งโดยตรงในตัวฟังก์ชัน หากเซิร์ฟเวอร์ย้ายไปที่อยู่อื่น นักพัฒนาต้องหาคำสั่งในโค้ด เปลี่ยนแปลง สร้างแอปพลิเคชันใหม่ และเผยแพร่รุ่นใหม่ ในแอปพลิเคชันที่มีสถาปัตยกรรมที่ถูกต้อง URL ดังกล่าวจะถูกนำไปไว้ในไฟล์การตั้งค่า ตัวแปรสภาพแวดล้อม หรือบริการการตั้งค่า
คำว่า“ตอกตะปูน”มีลักษณะทางอารมณ์มากกว่า: มันเน้นย้ำว่าค่าถูกใส่ไว้อย่างถาวร โดยไม่สามารถเปลี่ยนแทนได้อย่างรวดเร็ว ในสภาพแวดล้อมที่พูดภาษารัสเซีย ทั้งสองสำน่วนถูกใช้เป็นคำพ้องกันอย่างสมบูรณ์โดยมีนัยแนะทางลบ บางครั้ง hardcode ถูกเรียกอย่างเสียดังว่า“ค่าคงที่ถูกแยกออกไปเป็นค่าคงอีกอันจากค่าคงอีกอัน”
Hardcode เป็นแอนตีพะเทิร์นเพราะมันละเมิดหลักการบำรุงรักษา การทดสอบ และการขยายโค้ด ในโค้ดที่ค่าถูก“ตอกตะปูน” การเปลี่ยนแปลงสภาพแวดล้อม การออกแบบ หรือตรรกะโลจิการใดๆ จำเป็นต้องค้นหาและแทนที่ด้วยตนเองในโค้ดต้นฉบับ สิ่งนี้เพิ่มความเสี่ยงของข้อผิดพลาดและทำให้การพัฒนาช้าลง
พิจารณาผลกระทบอย่างเฉพาะเจาะของ hardcode โดยใช้ตัวอย่างแอปพลิเคชันมือถือทั่วไป หากระยะห่างของปุ่มทั้งหมดถูกกำหนดโดยตัวเลขในโค้ด แทนที่จะผ่านทรัพยากร — การเปลี่ยนแปลงการออกแบบจะต้องค้นหาทุกตำแหน่งที่ปรากฏและแทนที่ หาก URL จุดสิ้นสุดถูกกำหนดตายตัว — การสลับระหว่างสภาพแวดล้อม (dev, stage, prod) เป็นไปไม่ได้หากไม่ได้สร้างใหม่
| ผลกระทบ | คำอธิบาย | ระดับวิจารณ์ |
|---|---|---|
| ความยากในการบำรุง | การเปลี่ยนแปลงต้องค้นหาทั่วทั้งโค้ด | สูง |
| ข้อผิดพลาดเมื่อคัดลอก | ไม่พบและแทนที่ได้ทุกตำแหน่ง | สูง |
| ไม่สามารถทดสอบได้ | ไม่สามารถใส่ข้อมูลทดสอบได้ | ปานกลาง |
| ปัญหาการแปลภาษา | ข้อความในโค้ดไม่ได้รับการแปล | ปานกลาง |
| ความซับซ้อนของการตรวจโค้ด | ผู้ตรวจต้องจ๊าบริบทุกบริบท | ต่ำ |
ฟังก์ชันที่ใช้ตัวเลขมหาศจรรย์และคำสั่งตายตัว — ตัวอย่างคลาสสิกของ hardcode หนึ่งเดือนต่อมา ผู้เขียนจะไม่จำได้ว่า 18, 0.07 และ 2.5 หมายถึงอะไร หนึ่งปีต่อมา — ไม่มีใครในทีมกล้าเปลี่ยนตัวเลขเหล่านี้เพราะกลัวว่าจะทำลายลอจิก การนำค่าไปไว้ในค่าคงที่มีชื่อทำให้โค้ดสามารถอธิบายตัวเองได้
// แย่: ตัวเลขมหัศจรรย์และสตริง
fun calculatePrice(base: Double): Double {
val tax = base * 0.07
val tip = base * 0.15
val discount = if (base > 100) 10 else 0
return base + tax + tip - discount
}
URL ฐานข้อมูลที่ทำฮาร์ดโค้ดจะไม่อนุญาตให้เริ่มต้นการทดสอบบนฐานข้อมูลในหน่วยความจำที่อยู่ในหน่วยความจำ นักพัฒนาจะต้องเริ่มเซิร์ฟเวอร์ที่สมบูรณ์หรือแก้ไขโค้ดก่อนทดสอบ การนำคอนฟิกุรัชันออกจากโค้ดช่วยแก้ปัญหา: การทดสอบใช้พารามิเตอร์ทดสอบ production ใช้พารามิเตอร์จริง และโค้ดไม่เปลี่ยนแปลง
Hardcode เป็นแอนตีพะเทิร์น แต่มีข้อยกเว้นที่ชอบธรรมโดยเฉพาะเมื่อค่าตายตัวไม่เพียงแค่ยอมรับได้ แต่ยังเป็นที่ต้องการอีกด้วย ขอบเขตอยู่ตามแนวความสามารถในการเปลี่ยนแปลงได้: หากค่าไม่เคยหรือแทบไม่เคยเปลี่ยนแปลงในวงจรของแอปพลิเคชัน สามารถทำฮาร์ดโค้ดได้ หากมีโอกาสเปลี่ยนแปลง — นำไปไว้ในคอนฟิกุรัชัน
ค่าคงทางคณิตศาสตร์และฟิสิกส์ — ค่า Pi, ความเร็งแห่งแรงดึงดูด, จำนวนมิลลิวินาทีในหนึ่งวินาที — ปลอดภัยสำหรับ hardcode สิ่งเหล่านี้ถูกกำหนดโดยธรรมชาติหรือมาตรฐานและจะไม่เปลี่ยนแปลง ขนาดแอร์เรย์คงที่กำหนดโดยข้อกำหนดเฉพาะเจาะจง ก็สามารถตายตัวได้ แต่ต้องมีคำอธิบายเกี่ยวกับที่มาของตัวเลข
จำนวนมิลลิวินาทีในหนึ่งวินาทีเป็นค่าคงที่มั่นคงซึ่งกำหนดโดยมาตรฐานเวลา ไม่มีประโยชน์ในการนำไปไว้ใน config เพราะมันจะไม่มีวันเปลี่ยนแปลง อย่างไรก็ตาม แม้แต่ค่าคงเช่นนี้ก็ควรประกาศโดยใช้ชื่อที่เข้าใจได้ เพื่อไม่ให้โค้ดมี“ตัวเลขมหาศจรรย์”: แทนที่จะเป็น 1000 ให้เขียน MILLISECONDS_IN_SECOND
// Hardcode ที่ยอมรับได้: ค่าคงที่เสถียร
private const val MILLIS_IN_SECOND = 1000
private const val LOGIN_TIMEOUT_SECONDS = 30
fun formatDuration(ms: Long): String {
val seconds = ms / MILLIS_IN_SECOND
return "${seconds} sec."
}
มีหลายวิธีที่พิสูจน์แล้วในการหลีเลี่ยง hardcode แต่ละวิธีเหมาะสมกับประเภทของค่านั้น การเลือกทางเลือกขึ้นอยู่กับว่าค่าเปลี่ยนแปลงบ่อยแค่ใด และใครเปลี่ยนแปลง: นักพัฒนา, devops หรือผู้ใช้ปลายทาง
สำหรับ URL เซิร์ฟเวอร์, คีย์ API และ feature flag ให้ใช้ไฟล์การตั้งค่าในรูปแบบ JSON, YAML หรือ TOML บน Android คือ build.gradle กับ buildConfigField หรือ res/values/config.xml บน iOS — Info.plist หรือ xcconfig ไฟล์การตั้งค่าถูกสร้างร่วมกับแอปพลิเคชัน แต่สามารถแตกต่างกันได้สำหรับแผนการสร้างที่แตกต่างกัน
สำหรับข้อมูลลับ (token, รหัสผ่าน) และพารามิเตอร์สภาพแวดล้อม ให้ใช้ตัวแปรสภาพแวดล้อม ตัวแปรเหล่านี้จะไม่อยู่ในคลังโค้ด และสามารถแตกต่างกันได้บนเซิร์ฟเวอร์ dev, stage และ prod ในการพัฒนามือถือ ตัวแปรสภาพแวดล้อมมักถูกจำลองผ่านแผนการสร้าง Xcode หรือ build flavors ใน Gradle
ข้อความ, สี, ขนาด, รูปภาพควรถูกนำไปไว้ในไฟล์ทรัพยากร: strings.xml บน Android, Localizable.strings บน iOS, ไฟล์ ARB ใน Flutter ซึ่งช่วยให้การแปลภาษา, การปรับให้เข้ากับหน้าจอแตกต่างๆ และโหมดมืดง่ายขึ้น การเปลี่ยนข้อความในทรัพยากรไม่จำเป็นต้องเขียนโค้ดใหม่
<!-- Android: res/values/strings.xml -->
<resources>
<string name="app_name">MyApp</string>
<string name="api_base_url">https://api.example.com</string>
</resources>
สำหรับบริการและผู้ให้บริการ ให้ใช้ Dependency Injection ผ่าน Dagger, Hilt หรือ Koin บน Android, Swinject บน iOS โครงสร้าง DI ช่วยให้สามารถแทนที่การนำไปใช้ได้ทันที — สำหรับการทดสอบ, สำหรับสภาพแวดล้อมแตกต่างกัน, สำหรับผู้ใช้แตกต่างกัน นี่คือระดับสูงสุดของการทำนามธรรม, ที่การ“ตอกตะปูน”ค่าถูกแทนที่ด้วยการฉีดจากภายนอก
การรีแฟกเตอร์ฮาร์ดโค้ด เป็นกระบวนการนำค่าที่ตายตัวไปไว้ในคอนฟิกุรัชันหรือทรัพยากร นี่เป็นหนึ่งในการดำเนินการรีแฟกเตอร์ที่ปลอดภัยที่สุด หากดำเนินการอย่างมีระบบ ลำดับที่อธิบายด้านล่างมีความเหมาะสมสำหรับทุกภาษาและแพลตฟอร์ม
สามารถค้นหาได้ผ่าน IDE (Search in Project) หรือสคริปต์ ค้นหาคำสั่ง, URL, ตัวอักษรเฉพาะเจาะจง, ขนาด, กำหนดเวลา ให้ความสนใจเป็นพิเศษ — กับค่าที่ซ้ำกัน: หากตัวเลขเดียวกันปรากฏในห้าแห่ง, แสดงว่าเป็นผู้สมัครสำหรับค่าคง ใช้ grep หรือการค้นหาในตัวของ IDEA / Xcode
สำหรับแต่ละค่าที่พบให้สร้างค่าคงที่มีชื่อสะท้อนความหมาย จัดกลุ่มค่าคงตามโมดูลหรือคลาส ชื่อควรอธิบายความหมายของค่า ไม่ใช่วิธีการใช้งาน: API_TIMEOUT, ไม่ใช่ TIMEOUT_30 หลังจากแทนที่ ไม่ควรมีตัวเลขในโค้ดที่ไม่มีคำอธิบาย
// ก่อน: ตัวเลขมหัศจรรย์ 0.4
let cardHeight = screenHeight * 0.4
// หลัง: ค่าคงที่ที่มีชื่อ
private let cardHeightRatio: CGFloat = 0.4
let cardHeight = screenHeight * cardHeightRatio
หากค่าสามารถแตกต่างกันได้ระหว่างการสร้างหรือสภาพแวดล้อม — นำไปไว้ในไฟล์การตั้งค่าหรือทรัพยากรแอปพลิเคชัน สำหรับคำสั่ง ใช้ไฟล์การแปลภาษา สำหรับ URL — build config หรือ xcconfig สำหรับขนาด — ไฟล์ทรัพยากร (dimens.xml บน Android) ตรวจสอบว่าแอปพลิเคชันสร้างและทำงานได้อย่างถูกต้องหลังจากการนำออก
หลังจากการรีแฟกเตอร์ เขียนการทดสอบที่ตรวจสอบว่าคอนฟิกุรัชันถูกโหลดอย่างถูกต้อง และค่าต่างๆตรงกับที่คาดหวัง หากในอนาคตมีใครเปลี่ยนแปลง config การทดสอบจะชี้ไปที่ความไม่สอดคล้อง การทดสอบคอนฟิกุรัชัน — วิธีที่รวดเร็วและเชื่อถือได้ในการป้องกันการถดถอย
หลังจากนำไปไว้ใน config ตรวจสอบว่าทุกตำแหน่งที่ใช้ค่าเก่าอ้างอิงแหล่งเดียวกัน ลบโค้ดที่ถูกเสิมคำอธิบายและค่าคงเก่าที่ไม่ได้ใช้แล้ว สรุปการรีแฟกเตอร์ด้วย commit พร้อมข้อความที่อธิบายว่าค่าใดบ้างและถูกนำไปไว้ที่ไหน
คำถามที่พบบ่อย
Hardcode — การเขียนค่าโดยตรงในโค้ดต้นฉบับ แทนที่จะนำไปไว้ในคอนฟิกุรัชันหรือทรัพยากร ซึ่งทำให้โค้ดมีความยืดหยุ่นน้อยลงและเพิ่มความยากในการบำรุง
Hardcode ทำให้การเปลี่ยนแปลงพฤติกรรมของแอปพลิเคชันยากขึ้น ขัดขวางการทดสอบ สร้างการทำซ้ำ และเพิ่มความเสี่ยงของข้อผิดพลาดในการคัดลอก การเปลี่ยนแปลงค่าที่ทำฮาร์ดโค้ดจำเป็นต้องสร้างแอปพลิเคชันใหม่และเผยแพร่ใหม่
ยอมรับได้ สำหรับค่าคงทางคณิตศาสตร์, ค่าที่มั่นคงที่ไม่เปลี่ยนแปลงในวงจรของแอปพลิเคชัน, และสำหรับโปรโตกรแอมชั่วคราว ในการผลิตจริง, แม้แต่ค่าคงก็ควรถูกนำไปไว้ในตัวแปรที่มีชื่อ
ค้นหาตัวเลขมหาศจรรย์ทั้งหมดโดยการค้นหา, แทนที่ด้วยค่าคงที่มีชื่อ หรือนำไปไว้ในไฟล์การตั้งค่า เขียนการทดสอบที่ตรวจสอบการโหลดคอนฟิกุรัชัน ลบข้อมูลซ้ำ และทำ commit พร้อมคำอธิบายการเปลี่ยนแปลง
ค่าคงที่ — ค่าที่มีชื่อในโค้ด, สามารถเปลี่ยนแปลงได้ที่เดียว Hardcode — ค่าที่ไม่มีชื่อกระจายอยู่ในโค้ด แนวปฏิบัติที่ดี: ใช้ค่าคงที่มีชื่อพร้อมชื่อที่มีความหมายเสมอ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม