หมายเลขเวทย์มนตร์คืออะไร?
ทำไมจึงควรหลีกเลี่ยง
มีหลายกรณีที่เหมาะสมหรือไม่
หมายเลขเวทย์มนตร์คืออะไร?
ทำไมจึงควรหลีกเลี่ยง
มีหลายกรณีที่เหมาะสมหรือไม่
คำตอบ:
หมายเลขเวทย์มนตร์เป็นการใช้ตัวเลขในรหัสโดยตรง
ตัวอย่างเช่นถ้าคุณมี (ใน Java):
public class Foo {
public void setPassword(String password) {
// don't do this
if (password.length() > 7) {
throw new InvalidArgumentException("password");
}
}
}
สิ่งนี้ควรได้รับการ refactored ถึง:
public class Foo {
public static final int MAX_PASSWORD_SIZE = 7;
public void setPassword(String password) {
if (password.length() > MAX_PASSWORD_SIZE) {
throw new InvalidArgumentException("password");
}
}
}
มันช่วยปรับปรุงการอ่านรหัสและง่ายต่อการบำรุงรักษา ลองนึกภาพกรณีที่ฉันกำหนดขนาดของฟิลด์รหัสผ่านใน GUI ถ้าฉันใช้หมายเลขเวทย์เมื่อใดก็ตามที่ขนาดสูงสุดเปลี่ยนไปฉันต้องเปลี่ยนรหัสสองตำแหน่ง หากฉันลืมสิ่งนี้สิ่งนี้จะนำไปสู่ความไม่สอดคล้องกัน
ตั้ง JDK ที่เต็มไปด้วยตัวอย่างเช่นในInteger
, Character
และMath
ชั้นเรียน
PS: เครื่องมือวิเคราะห์แบบคงที่เช่น FindBugs และ PMD ตรวจจับการใช้ตัวเลขเวทย์มนตร์ในรหัสของคุณและแนะนำการปรับโครงสร้างใหม่
TRUE
/ FALSE
)
Magic Number เป็นค่าฮาร์ดโค้ดที่อาจเปลี่ยนแปลงได้ในภายหลัง แต่อาจยากที่จะอัปเดต
ตัวอย่างเช่นสมมติว่าคุณมีหน้าเว็บที่แสดงคำสั่งซื้อ 50 รายการสุดท้ายในหน้าภาพรวม "คำสั่งซื้อของคุณ" 50 คือหมายเลขเมจิกที่นี่เนื่องจากไม่ได้กำหนดตามแบบแผนหรือแบบแผนมันเป็นหมายเลขที่คุณประกอบขึ้นด้วยเหตุผลที่ระบุไว้ในข้อมูลจำเพาะ
ตอนนี้สิ่งที่คุณทำคือคุณมี 50 ในสถานที่ที่แตกต่างกัน - สคริปต์ SQL ของคุณ ( SELECT TOP 50 * FROM orders
), เว็บไซต์ของคุณ (50 คำสั่งซื้อล่าสุดของคุณ) เข้าสู่ระบบคำสั่งซื้อของคุณ ( for (i = 0; i < 50; i++)
) และสถานที่อื่น ๆ
ตอนนี้จะเกิดอะไรขึ้นเมื่อมีคนตัดสินใจเปลี่ยน 50 เป็น 25 หรือ 75 หรือ 153? ตอนนี้คุณต้องแทนที่ 50 ในสถานที่ทั้งหมดและคุณมีแนวโน้มที่จะพลาด ค้นหา / แทนที่อาจใช้งานไม่ได้เพราะ 50 อาจใช้สำหรับสิ่งอื่น ๆ และการแทนที่ 50 ด้วย 25 อาจมีผลข้างเคียงที่ไม่ดีอื่น ๆ (เช่นการSession.Timeout = 50
โทรของคุณซึ่งตั้งไว้ที่ 25 และผู้ใช้เริ่มรายงานการหมดเวลาบ่อยเกินไป)
นอกจากนี้รหัสยังยากที่จะเข้าใจเช่น " if a < 50 then bla
" - หากคุณพบว่าในระหว่างการทำงานที่ซับซ้อนผู้พัฒนารายอื่นที่ไม่คุ้นเคยกับรหัสอาจถามตัวเองว่า "WTF is 50 ???"
นั่นเป็นสาเหตุที่ดีที่สุดที่จะมีตัวเลขที่คลุมเครือและไม่แน่นอนในที่เดียว - " const int NumOrdersToDisplay = 50
" เพราะนั่นทำให้โค้ดอ่านง่ายขึ้น (" if a < NumOrdersToDisplay
" มันก็หมายความว่าคุณต้องเปลี่ยนมันในที่ที่กำหนดไว้อย่างดีเพียงแห่งเดียวเท่านั้น
สถานที่ที่มีหมายเลข Magic เหมาะสมทุกอย่างที่กำหนดผ่านมาตรฐานคือSmtpClient.DefaultPort = 25
หรือTCPPacketSize = whatever
(ไม่แน่ใจว่าเป็นมาตรฐาน) นอกจากนี้ทุกอย่างที่กำหนดไว้ภายใน 1 ฟังก์ชั่นอาจยอมรับได้ แต่ขึ้นอยู่กับบริบท
SmtpClient.DefaultPort = 25
อาจจะเป็นที่ชัดเจนเอ้อSmtpClient.DefaultPort = DEFAULT_SMTP_PORT
กว่า
25
แอปพลิเคชันทั้งหมดและตรวจสอบให้แน่ใจว่าคุณเปลี่ยนเฉพาะสิ่งที่เกิดขึ้น25
สำหรับพอร์ต SMTP ไม่ใช่ 25 ของเช่นความกว้างของคอลัมน์ตารางหรือจำนวน ของระเบียนที่จะแสดงในหน้า
IANA
แต่จำนวนได้รับมอบหมายจาก
คุณลองดูที่รายการ Wikipedia เพื่อหาหมายเลขเวทย์มนตร์หรือไม่?
มันมีรายละเอียดเล็กน้อยเกี่ยวกับวิธีการอ้างอิงหมายเลขเวทย์ นี่คือคำพูดเกี่ยวกับหมายเลขเวทย์มนตร์ว่าเป็นการฝึกเขียนโปรแกรมไม่ดี
คำวิเศษหมายเลขยังหมายถึงการฝึกเขียนโปรแกรมที่ไม่ดีของการใช้ตัวเลขโดยตรงในรหัสที่มาโดยไม่มีคำอธิบาย ในกรณีส่วนใหญ่สิ่งนี้ทำให้โปรแกรมอ่านเข้าใจและบำรุงรักษาได้ยากขึ้น แม้ว่าไกด์ส่วนใหญ่จะสร้างข้อยกเว้นสำหรับตัวเลขศูนย์และหนึ่ง แต่ก็เป็นความคิดที่ดีที่จะกำหนดตัวเลขอื่น ๆ ทั้งหมดในรหัสเป็นค่าคงที่ที่มีชื่อ
Magic: ความหมายที่ไม่รู้จัก
Symbolic Constant -> ให้ทั้งบริบทความหมายและบริบทที่ถูกต้องสำหรับการใช้งาน
ความหมาย: ความหมายหรือวัตถุประสงค์ของสิ่ง
"สร้างค่าคงที่ตั้งชื่อตามความหมายและแทนที่ด้วย" - Martin Fowler
ประการแรกตัวเลขเวทมนตร์ไม่ได้เป็นเพียงตัวเลข ค่าพื้นฐานใด ๆ อาจเป็น "เวทมนต์" ค่าพื้นฐานคือรายการที่ปรากฏเช่นจำนวนเต็ม, reals, doubles, float, วันที่, สตริง, booleans, ตัวอักษรและอื่น ๆ ปัญหาไม่ได้เป็นประเภทข้อมูล แต่เป็นแง่มุม "วิเศษ" ของค่าตามที่ปรากฏในข้อความรหัสของเรา
"เวทมนต์" หมายถึงอะไร? เพื่อความแม่นยำ: โดย "เวทมนต์" เราตั้งใจจะชี้ไปที่ความหมาย (ความหมายหรือวัตถุประสงค์) ของค่าในบริบทของรหัสของเรา ไม่เป็นที่ทราบไม่สามารถเข้าใจได้ไม่ชัดเจนหรือสับสน นี่คือความคิดของ "เวทมนตร์" ค่าพื้นฐานไม่ใช่เวทมนตร์เมื่อความหมายเชิงความหมายหรือจุดประสงค์ของการเป็นอยู่นั้นเป็นที่รู้จักอย่างรวดเร็วและง่ายดายชัดเจนและเข้าใจ (ไม่สับสน) จากบริบทแวดล้อมโดยไม่มีคำช่วยเหลือพิเศษ (เช่นค่าคงที่เชิงสัญลักษณ์)
ดังนั้นเราจึงระบุหมายเลขเวทย์มนตร์โดยการวัดความสามารถของเครื่องอ่านรหัสที่จะรู้ชัดเจนและเข้าใจความหมายและวัตถุประสงค์ของค่าพื้นฐานจากบริบทโดยรอบ ผู้อ่านที่รู้จักน้อยกว่าชัดเจนน้อยลงและสับสนมากขึ้นคือ "พื้นฐาน" ของค่าพื้นฐานที่มากขึ้น
เรามีสองสถานการณ์สำหรับค่าพื้นฐานมายากลของเรา เฉพาะที่สองมีความสำคัญหลักสำหรับโปรแกรมเมอร์และรหัส:
การพึ่งพาอย่างมากของ "เวทย์มนตร์" คือวิธีที่ค่าพื้นฐานเดียว (เช่นจำนวน) ไม่มีความหมายทั่วไปที่รู้จักกัน (เช่น Pi) แต่มีความหมายที่รู้จักในระดับท้องถิ่น (เช่นโปรแกรมของคุณ) ซึ่งไม่ชัดเจนจากบริบททั้งหมดหรืออาจถูกทารุณกรรม ในบริบทที่ดีหรือไม่ดี
ความหมายของภาษาโปรแกรมส่วนใหญ่จะไม่อนุญาตให้เราใช้ค่าพื้นฐานเดียวยกเว้น (บางที) เป็นข้อมูล (เช่นสารบัญ) เมื่อเราพบ "หมายเลขเวทมนตร์" เรามักจะทำเช่นนั้นในบริบท ดังนั้นคำตอบ
"ฉันจะแทนที่หมายเลขเวทย์มนตร์นี้ด้วยค่าคงที่เชิงสัญลักษณ์ได้หรือไม่"
คือ:
"คุณสามารถประเมินและเข้าใจความหมายของตัวเลขอย่างรวดเร็วได้อย่างไรในบริบทของมัน"
ด้วยความคิดนี้เราสามารถเห็นได้อย่างรวดเร็วว่าจำนวนเช่น Pi (3.14159) ไม่ใช่ "หมายเลขเวทมนตร์" เมื่ออยู่ในบริบทที่เหมาะสม (เช่น 2 x 3.14159 x รัศมีหรือ 2 * Pi * r) ที่นี่หมายเลข 3.14159 เป็นที่รู้จักทางจิตใจ Pi โดยไม่มีตัวระบุค่าคงที่เชิงสัญลักษณ์
อย่างไรก็ตามโดยทั่วไปเราจะแทนที่ 3.14159 ด้วยตัวระบุค่าคงที่เชิงสัญลักษณ์เช่น Pi เนื่องจากความยาวและความซับซ้อนของตัวเลข ด้านของความยาวและความซับซ้อนของ Pi (ควบคู่กับความต้องการความถูกต้อง) มักจะหมายถึงตัวบ่งชี้เชิงสัญลักษณ์ การรับรู้ "Pi" เป็นชื่อเป็นเพียงโบนัสที่สะดวก แต่ไม่ใช่เหตุผลหลักที่ทำให้ค่าคงที่
วางค่าคงที่ทั่วไปเช่น Pi ไว้ให้เราจดจ่อกับตัวเลขด้วยความหมายพิเศษเป็นหลัก แต่ความหมายเหล่านั้นถูก จำกัด อยู่ในจักรวาลของระบบซอฟต์แวร์ ตัวเลขดังกล่าวอาจเป็น "2" (เป็นค่าจำนวนเต็มพื้นฐาน)
หากฉันใช้หมายเลข 2 ด้วยตัวเองคำถามแรกของฉันอาจเป็น: "2" หมายถึงอะไร ความหมายของ "2" โดยตัวของมันเองนั้นไม่เป็นที่รู้จักและไม่สามารถหยั่งรู้ได้โดยไม่มีบริบททำให้การใช้งานนั้นไม่ชัดเจนและสับสน แม้ว่าจะมีเพียงแค่ "2" ในซอฟต์แวร์ของเราจะไม่เกิดขึ้นเนื่องจากความหมายของภาษา แต่เราต้องการเห็นว่า "2" นั้นไม่มีความหมายพิเศษหรือมีจุดประสงค์ที่ชัดเจนเพียงอย่างเดียว
มาใส่คำว่า "2" ของเราไว้ในบริบทของ: padding := 2
โดยบริบทคือ "GUI Container" ในบริบทนี้ความหมายของ 2 (เป็นพิกเซลหรือหน่วยกราฟิกอื่น ๆ ) ให้เราคาดเดาอย่างรวดเร็วของความหมาย (ความหมายและวัตถุประสงค์) เราอาจหยุดที่นี่และบอกว่า 2 ใช้ได้ในบริบทนี้และไม่มีอะไรที่เราต้องรู้ อย่างไรก็ตามบางทีในจักรวาลซอฟต์แวร์ของเรานี่ไม่ใช่เรื่องทั้งหมด มีมากกว่านั้น แต่ "padding = 2" เนื่องจากบริบทไม่สามารถเปิดเผยได้
มาทำท่าต่อไปว่า 2 ในฐานะที่เป็นช่องว่างภายในพิกเซลในโปรแกรมของเรานั้นมีความหลากหลาย "default_padding" ทั่วทั้งระบบของเรา ดังนั้นการเขียนคำสั่งpadding = 2
ไม่ดีพอ ความคิดของ "เริ่มต้น" จะไม่เปิดเผย เฉพาะเมื่อฉันเขียน: padding = default_padding
เป็นบริบทและที่อื่น ๆ : default_padding = 2
ฉันจะตระหนักถึงความหมายที่ดีขึ้นและสมบูรณ์ขึ้น (2 ความหมายและความหมาย) ในระบบของเราหรือไม่
ตัวอย่างข้างต้นค่อนข้างดีเพราะ "2" ด้วยตัวเองอาจเป็นอะไรก็ได้ เฉพาะเมื่อเรา จำกัด ช่วงและขอบเขตของความเข้าใจกับ "โปรแกรมของฉัน" โดยที่ 2 คือdefault_padding
ในส่วนของ GUI UX ของ "โปรแกรมของฉัน" ในที่สุดเราก็ใช้คำว่า "2" ในบริบทที่เหมาะสม ที่นี่ "2" เป็นหมายเลข "เวทย์มนตร์" ซึ่งแยกออกเป็นค่าคงที่เชิงสัญลักษณ์default_padding
ภายในบริบทของ GUI UX ของ "โปรแกรมของฉัน" เพื่อให้สามารถใช้งานdefault_padding
ได้อย่างรวดเร็วในบริบทที่ยิ่งใหญ่ของโค้ดที่ล้อมรอบ
ดังนั้นค่าพื้นฐานใด ๆ ที่มีความหมาย (ความหมายและวัตถุประสงค์) ไม่สามารถเข้าใจได้อย่างรวดเร็วและเพียงพอจึงเป็นตัวเลือกที่ดีสำหรับค่าคงที่เชิงสัญลักษณ์ในสถานที่ของค่าพื้นฐาน (เช่นเลขกล)
ตัวเลขในระดับอาจมีความหมายเช่นกัน ตัวอย่างเช่นหลอกว่าเรากำลังสร้างเกม D&D ที่เรามีแนวคิดเกี่ยวกับสัตว์ประหลาด วัตถุมอนสเตอร์ของเรามีคุณสมบัติที่เรียกว่าlife_force
ซึ่งเป็นจำนวนเต็ม ตัวเลขมีความหมายที่ไม่สามารถรู้หรือชัดเจนได้หากไม่มีคำที่สื่อความหมาย ดังนั้นเราเริ่มโดยพลการพูดว่า:
จากค่าคงที่เชิงสัญลักษณ์ข้างต้นเราเริ่มเห็นภาพจิตของความมีชีวิตชีวาความตายและ "ความไม่แน่นอน" (และความเป็นไปได้ของการแตกหรือผลที่ตามมา) สำหรับสัตว์ประหลาดของเราในเกม D&D หากไม่มีคำเหล่านี้ (ค่าคงที่เชิงสัญลักษณ์) เราจะเหลือเพียงตัวเลข-10 .. 10
เท่านั้น เพียงแค่ช่วงโดยไม่มีคำใบเราในสถานที่ของความสับสนอาจจะดีและอาจมีข้อผิดพลาดในเกมของเราถ้าชิ้นส่วนที่แตกต่างกันของเกมที่มีการอ้างอิงในสิ่งที่ช่วงของตัวเลขที่หมายถึงการดำเนินการต่างๆเช่นหรือattack_elves
seek_magic_healing_potion
ดังนั้นเมื่อค้นหาและพิจารณาการเปลี่ยน "หมายเลขมายากล" เราต้องการถามคำถามที่มีจุดประสงค์อย่างเต็มที่เกี่ยวกับตัวเลขภายในบริบทของซอฟต์แวร์ของเราและแม้ว่าตัวเลขจะมีความสัมพันธ์กันในเชิงความหมาย
ตรวจสอบคำถามที่เราควรถาม:
คุณอาจมีหมายเลขมายากลหาก ...
ตรวจสอบค่าพื้นฐานคงที่อย่างชัดแจ้งแบบสแตนด์อโลนในข้อความรหัสของคุณ ถามคำถามแต่ละข้ออย่างช้าๆและไตร่ตรองเกี่ยวกับคุณค่าดังกล่าวแต่ละตัวอย่าง พิจารณาความแข็งแกร่งของคำตอบของคุณ หลายครั้งคำตอบไม่ใช่ขาวดำ แต่มีเฉดสีของความหมายและวัตถุประสงค์ที่เข้าใจผิดความเร็วในการเรียนรู้และความเร็วในการเข้าใจ นอกจากนี้ยังมีความต้องการที่จะดูว่ามันเชื่อมต่อกับเครื่องซอฟต์แวร์รอบ ๆ มันได้อย่างไร
ในที่สุดคำตอบสำหรับการเปลี่ยนคือคำตอบของความแข็งแกร่งหรือจุดอ่อนของผู้อ่านเพื่อทำการเชื่อมต่อ (เช่น "รับ") ยิ่งพวกเขาเข้าใจความหมายและวัตถุประสงค์ได้เร็วเท่าไรคุณก็ยิ่งมีเวทย์มนตร์น้อยเท่านั้น
สรุป: แทนที่ค่าพื้นฐานด้วยค่าคงที่เชิงสัญลักษณ์เฉพาะเมื่อเวทย์มนตร์มีขนาดใหญ่พอที่จะทำให้ตรวจจับข้อบกพร่องที่เกิดจากการสับสนได้ยาก
หมายเลขอาถรรพ์คือลำดับของอักขระที่จุดเริ่มต้นของรูปแบบไฟล์หรือการแลกเปลี่ยนโปรโตคอล หมายเลขนี้ทำหน้าที่ตรวจสอบสติ
ตัวอย่าง: เปิดไฟล์ GIF ใด ๆ คุณจะเห็นตอนเริ่มต้น: GIF89 "GIF89" เป็นหมายเลขเวทย์มนตร์
โปรแกรมอื่นสามารถอ่านอักขระสองสามตัวแรกของไฟล์และระบุ GIF ได้อย่างถูกต้อง
อันตรายคือข้อมูลไบนารีแบบสุ่มสามารถมีอักขระเหมือนกันได้ แต่มันไม่น่าเป็นไปได้
สำหรับการแลกเปลี่ยนโพรโทคอลคุณสามารถใช้เพื่อระบุว่า 'ข้อความ' ปัจจุบันที่ถูกส่งไปยังคุณอย่างรวดเร็วเสียหายหรือไม่ถูกต้อง
ตัวเลขเวทย์มนตร์ยังมีประโยชน์
ในการเขียนโปรแกรม "หมายเลขเวทมนตร์" คือค่าที่ควรได้รับชื่อสัญลักษณ์ แต่ถูกแทนที่ลงในรหัสเป็นตัวอักษรโดยปกติจะอยู่ในสถานที่มากกว่าหนึ่งแห่ง
มันไม่ดีด้วยเหตุผลเดียวกัน SPOT (Single Point of Truth) ก็ดี: ถ้าคุณต้องการเปลี่ยนค่าคงที่นี้ในภายหลังคุณจะต้องค้นหารหัสของคุณเพื่อค้นหาทุกตัวอย่าง มันก็ไม่ดีเพราะมันอาจไม่ชัดเจนสำหรับโปรแกรมเมอร์คนอื่น ๆ ที่ตัวเลขนี้แสดงถึงดังนั้นจึงเป็น "เวทมนต์"
บางครั้งผู้คนใช้การกำจัดจำนวนเวทย์มนตร์เพิ่มเติมโดยการย้ายค่าคงที่เหล่านี้เป็นไฟล์แยกเพื่อทำหน้าที่กำหนดค่า บางครั้งสิ่งนี้มีประโยชน์ แต่ก็สามารถสร้างความซับซ้อนได้มากกว่าที่คุ้มค่า
(foo[i]+foo[i+1]+foo[i+2]+1)/3
นั้นอาจได้รับการประเมินเร็วกว่าลูป หากมีการแทนที่3
โดยไม่ต้องเขียนโค้ดใหม่เป็นลูปใครบางคนที่เห็นว่าITEMS_TO_AVERAGE
ถูกกำหนด3
อาจคิดว่าพวกเขาสามารถเปลี่ยนเป็น5
และมีรหัสรายการเฉลี่ยมากขึ้น ในทางตรงกันข้ามคนที่ดูการแสดงออกด้วยตัวอักษร3
จะรู้ว่า3
จำนวนนั้นเป็นรายการที่รวมเข้าด้วยกัน
เลขอาถรรพ์อาจเป็นตัวเลขที่มีความหมายพิเศษแบบฮาร์ดโค้ด ตัวอย่างเช่นฉันเคยเห็นระบบที่มีการบันทึกรหัส> 0 ได้รับการปฏิบัติตามปกติ 0 ตัวเองคือ "บันทึกใหม่" -1 คือ "นี่คือราก" และ -99 เป็น "สิ่งนี้ถูกสร้างขึ้นในราก" 0 และ -99 จะทำให้ WebService จัดหา ID ใหม่
สิ่งที่แย่เกี่ยวกับเรื่องนี้คือคุณกำลังนำพื้นที่ (จำนวนเต็มที่ลงนามแล้วสำหรับรหัสบันทึก) มาใช้เพื่อความสามารถพิเศษ บางทีคุณอาจไม่ต้องการสร้างบันทึกด้วย ID 0 หรือลบ ID แต่ถ้าไม่ใช่ทุกคนที่ดูรหัสหรือที่ฐานข้อมูลอาจสะดุดกับเรื่องนี้และสับสนในตอนแรก มันไปโดยไม่บอกว่าค่าพิเศษเหล่านั้นไม่มีเอกสารที่ดี
เนื้อหานับว่า22, 7, -12 และ 620ถือเป็นตัวเลขเวทย์มนตร์เช่นกัน ;-)
ปัญหาที่ไม่ได้รับการกล่าวถึงด้วยการใช้หมายเลขเวทย์มนตร์ ...
หากคุณมีจำนวนมากอัตราต่อรองเป็นสิ่งที่ดีพอสมควรที่คุณมีวัตถุประสงค์ที่แตกต่างกันสองประการที่คุณใช้ตัวเลขเวทย์มนตร์สำหรับค่าที่เกิดขึ้นจะเหมือนกัน
และจากนั้นคุณจะต้องเปลี่ยนค่า ... เพื่อจุดประสงค์เดียวเท่านั้น
ฉันถือว่านี่เป็นคำตอบสำหรับคำตอบของคุณสำหรับคำถามก่อนหน้านี้ ในการเขียนโปรแกรมหมายเลขมายากลคือค่าคงตัวตัวเลขที่ฝังอยู่ซึ่งปรากฏขึ้นโดยไม่มีคำอธิบาย หากปรากฏในสถานที่สองแห่งที่แตกต่างกันก็สามารถนำไปสู่สถานการณ์ที่มีการเปลี่ยนแปลงอินสแตนซ์หนึ่งและไม่อีก ด้วยเหตุผลทั้งสองนี้สิ่งสำคัญคือการแยกและกำหนดค่าคงที่ตัวเลขนอกสถานที่ที่ใช้
ฉันมักจะใช้คำว่า "หมายเลขเวทมนตร์" แตกต่างกันเป็นค่าที่คลุมเครือเก็บไว้ในโครงสร้างข้อมูลซึ่งสามารถตรวจสอบได้เป็นการตรวจสอบความถูกต้องอย่างรวดเร็ว ตัวอย่างเช่นไฟล์ gzip มี 0x1f8b08 เป็นสามไบต์แรกไฟล์คลาส Java เริ่มต้นด้วย 0xcafebabe เป็นต้น
คุณมักจะเห็นเลขอาถรรพ์ที่ฝังอยู่ในรูปแบบไฟล์เนื่องจากไฟล์สามารถส่งไปได้ค่อนข้างชัดเจนและสูญเสียข้อมูลเมตาเกี่ยวกับวิธีการสร้าง อย่างไรก็ตามบางครั้งก็ใช้หมายเลขเวทย์มนตร์สำหรับโครงสร้างข้อมูลในหน่วยความจำเช่นการเรียก ioctl ()
การตรวจสอบหมายเลขมายากลอย่างรวดเร็วก่อนการประมวลผลไฟล์หรือโครงสร้างข้อมูลทำให้สามารถส่งสัญญาณข้อผิดพลาดได้เร็วกว่าการประมวลผลที่มีความยาวมากเพื่อแจ้งให้ทราบว่าอินพุตเป็น balderdash ที่สมบูรณ์
เป็นที่น่าสังเกตว่าบางครั้งคุณต้องการตัวเลขที่ "ไม่กำหนดรหัส" ในรหัสของคุณ มีคนที่มีชื่อเสียงจำนวนมากรวมถึง 0x5F3759DF ซึ่งใช้ในอัลกอริทึมรากที่สองที่ได้รับการปรับปรุง
ในบางกรณีที่ฉันพบว่าจำเป็นต้องใช้ Magic Numbers เช่นนั้นฉันตั้งให้พวกเขาเป็น const ในรหัสของฉันและจัดทำเอกสารเกี่ยวกับสาเหตุที่ใช้งานวิธีการทำงานและที่มาของพวกเขา
สิ่งที่เกี่ยวกับการเริ่มต้นตัวแปรที่ด้านบนของชั้นเรียนด้วยค่าเริ่มต้น? ตัวอย่างเช่น:
public class SomeClass {
private int maxRows = 15000;
...
// Inside another method
for (int i = 0; i < maxRows; i++) {
// Do something
}
public void setMaxRows(int maxRows) {
this.maxRows = maxRows;
}
public int getMaxRows() {
return this.maxRows;
}
ในกรณีนี้ 15,000 หมายเลขมายากล (ตาม CheckStyles) สำหรับฉันการตั้งค่าเริ่มต้นก็โอเค ฉันไม่ต้องการทำ:
private static final int DEFAULT_MAX_ROWS = 15000;
private int maxRows = DEFAULT_MAX_ROWS;
นั่นทำให้อ่านยากขึ้นหรือไม่ ฉันไม่เคยพิจารณาเรื่องนี้จนกว่าฉันจะติดตั้ง CheckStyles
static final
ค่าคงที่มากเกินไปเมื่อคุณใช้พวกมันในวิธีเดียว final
ประกาศตัวแปรที่ด้านบนของวิธีการที่เป็น IMHO อ่านได้มากขึ้น
@ eed3si9n: ฉันอยากจะแนะนำว่า '1' เป็นหมายเลขเวทย์มนตร์ :-)
หลักการที่เกี่ยวข้องกับตัวเลขเวทย์มนตร์คือความจริงทุกข้อเกี่ยวกับรหัสของคุณควรถูกประกาศครั้งเดียว หากคุณใช้หมายเลขเวทย์มนตร์ในรหัสของคุณ (เช่นตัวอย่างความยาวรหัสผ่านที่ @marcio มอบให้คุณสามารถทำซ้ำความจริงนั้นได้อย่างง่ายดายและเมื่อคุณเข้าใจถึงความจริงที่ว่าคุณมีปัญหาในการบำรุงรักษา
factorial n = if n == BASE_CASE then BASE_VALUE else n * factorial (n - RECURSION_INPUT_CHANGE); RECURSION_INPUT_CHANGE = 1; BASE_CASE = 0; BASE_VALUE = 1
สิ่งที่เกี่ยวกับตัวแปรส่งคืน
ฉันเป็นพิเศษพบว่ามันมีความท้าทายเมื่อใช้วิธีการจัดเก็บ
ลองนึกภาพกระบวนงานที่เก็บไว้ถัดไป (ฉันเข้าใจว่าผิดเพื่อแสดงตัวอย่าง):
int procGetIdCompanyByName(string companyName);
จะส่งคืนรหัสของ บริษัท หากมีอยู่ในตารางที่ระบุ มิฉะนั้นจะส่งคืน -1 ยังไงก็เถอะมันเป็นหมายเลขเวทย์มนตร์ คำแนะนำที่ฉันได้อ่านจนถึงตอนนี้บอกว่าฉันจะต้องออกแบบบางอย่างเช่น:
int procGetIdCompanyByName(string companyName, bool existsCompany);
โดยวิธีการสิ่งที่มันควรจะกลับมาถ้า บริษัท ไม่อยู่? ตกลง: มันจะตั้งค่ามีอยู่บริษัทเป็นเท็จแต่จะส่งคืน -1 ด้วย
ตัวเลือก Antoher คือการสร้างสองฟังก์ชั่นแยก:
bool procCompanyExists(string companyName);
int procGetIdCompanyByName(string companyName);
ดังนั้นเงื่อนไขล่วงหน้าสำหรับขั้นตอนการจัดเก็บที่สองคือ บริษัท นั้นมีอยู่
แต่ฉันกลัวการเห็นพ้องด้วยเพราะในระบบนี้ บริษัท สามารถสร้างโดยผู้ใช้รายอื่น
บรรทัดล่างคือ: คุณคิดอย่างไรเกี่ยวกับการใช้ "หมายเลขเวทมนตร์" ที่ค่อนข้างเป็นที่รู้จักและปลอดภัยที่จะบอกว่ามีบางสิ่งที่ไม่ประสบความสำเร็จหรือมีบางสิ่งที่ไม่มีอยู่จริง?
ข้อดีอีกอย่างของการแยกเลขอาถรรพ์เป็นค่าคงที่ช่วยให้สามารถบันทึกข้อมูลทางธุรกิจได้อย่างชัดเจน
public class Foo {
/**
* Max age in year to get child rate for airline tickets
*
* The value of the constant is {@value}
*/
public static final int MAX_AGE_FOR_CHILD_RATE = 2;
public void computeRate() {
if (person.getAge() < MAX_AGE_FOR_CHILD_RATE) {
applyChildRate();
}
}
}
const myNum = 22; const number = myNum / 11;
ตอนนี้ 11 คนของฉันอาจเป็นคนหรือขวดเบียร์หรืออะไรทำนองนั้นแทนฉันจะเปลี่ยน 11 เป็นค่าคงที่ เช่นผู้อยู่อาศัย