กำจัดหมายเลขเวทย์มนตร์: เมื่อไหร่เวลาที่จะพูดว่า "ไม่"


36

เราทุกคนทราบดีว่าเลขอาถรรพ์ (ค่าฮาร์ดโค้ด) สามารถสร้างความหายนะในโปรแกรมของคุณโดยเฉพาะเมื่อถึงเวลาที่จะแก้ไขส่วนของรหัสที่ไม่มีความคิดเห็น แต่คุณจะวาดเส้นที่ไหน

ตัวอย่างเช่นหากคุณมีฟังก์ชันที่คำนวณจำนวนวินาทีระหว่างสองวันคุณจะเปลี่ยนหรือไม่

seconds = num_days * 24 * 60 * 60

กับ

seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE

ณ จุดใดที่คุณตัดสินใจว่ามันชัดเจนอย่างชัดเจนว่าค่าฮาร์ดโค้ดมีความหมายอย่างไรและทิ้งไว้คนเดียว?


2
ทำไมไม่ลองแทนที่การคำนวณด้วยฟังก์ชันหรือมาโครเพื่อให้โค้ดของคุณดูเป็นอย่างไรseconds = CALC_SECONDS(num_days);
FrustratedWithFormsDesigner

15
TimeSpan.FromDays(numDays).Seconds;
ไม่มีใคร

18
@oosterwal: ด้วยทัศนคตินั้น ( HOURS_PER_DAY will never need to be altered) คุณจะไม่มีวันเข้ารหัสสำหรับซอฟต์แวร์ที่ติดตั้งบนดาวอังคาร : P
FrustratedWithFormsDesigner

23
ฉันจะลดจำนวนค่าคงที่ให้เหลือแค่ SECONDS_PER_DAY = 86400 ทำไมต้องคำนวณสิ่งที่จะไม่เปลี่ยนแปลง?
JohnFx

17
กระโดดวินาที?
จอห์น

คำตอบ:


40

มีสองเหตุผลในการใช้ค่าคงที่เชิงสัญลักษณ์แทนตัวอักษรตัวเลข:

  1. เพื่อลดความยุ่งยากในการบำรุงรักษาหากจำนวนเวทย์เปลี่ยนไป สิ่งนี้ใช้ไม่ได้กับตัวอย่างของคุณ ไม่น่าเป็นไปได้อย่างยิ่งที่จำนวนวินาทีในหนึ่งชั่วโมงหรือจำนวนชั่วโมงในหนึ่งวันจะเปลี่ยนไป

  2. เพื่อปรับปรุงการอ่าน การแสดงออก "24 * 60 * 60" นั้นชัดเจนสำหรับทุกคน "SECONDS_PER_DAY" นั้นเหมือนกัน แต่ถ้าคุณกำลังตามล่าข้อบกพร่องคุณอาจต้องไปตรวจสอบว่า SECONDS_PER_DAY นั้นได้กำหนดไว้อย่างถูกต้อง มีค่าในช่วงสั้น ๆ

สำหรับตัวเลขเวทย์มนตร์ที่ปรากฏครั้งเดียวและไม่ขึ้นอยู่กับโปรแกรมอื่น ๆ การตัดสินใจว่าจะสร้างสัญลักษณ์สำหรับหมายเลขนั้นเป็นเรื่องของรสนิยมหรือไม่ หากมีข้อสงสัยใด ๆ ไปข้างหน้าและสร้างสัญลักษณ์

อย่าทำอย่างนี้:

public static final int THREE = 3;

3
+1 @kevin cline: ฉันเห็นด้วยกับจุดของคุณเกี่ยวกับการล่าสัตว์บั๊ก ประโยชน์เพิ่มเติมที่ฉันเห็นในการใช้ค่าคงที่ที่มีชื่อโดยเฉพาะอย่างยิ่งเมื่อทำการดีบั๊กคือหากพบว่าค่าคงที่ถูกกำหนดอย่างไม่ถูกต้องคุณจะต้องเปลี่ยนรหัสหนึ่งชิ้นแทนที่จะค้นหาผ่านโครงการทั้งหมดสำหรับเหตุการณ์ทั้งหมดของการนำไปใช้อย่างไม่ถูกต้อง ราคา.
oosterwal

40
หรือแย่ยิ่งกว่า:publid final int FOUR = 3;
gablin

3
โอ้ที่รักคุณต้องทำงานกับผู้ชายคนเดียวกับที่ฉันเคยทำงานด้วย
quick_now

2
@gablin: เพื่อความยุติธรรมมันค่อนข้างมีประโยชน์สำหรับผับที่มีฝาปิด
Alan Pearce

10
ฉันเคยเห็นสิ่งนี้แล้ว: public static int THREE = 3;... ทราบ - ไม่final!
สตีเฟ่นซี

29

ฉันจะรักษากฎของการไม่เคยมีหมายเลขมายากล

ในขณะที่

seconds = num_days * 24 * 60 * 60

สามารถอ่านได้อย่างสมบูรณ์แบบที่สุดของเวลาส่วนใหญ่หลังจากเขียนโค้ดเป็นเวลา 10 ชั่วโมงต่อวันเป็นเวลาสามหรือสี่สัปดาห์ในโหมดขบเคี้ยว

seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE

อ่านง่ายกว่ามาก

ข้อเสนอแนะของ FrustratedWithFormsDesigner ดีกว่า:

seconds = num_days * DAYS_TO_SECOND_FACTOR

หรือดีกว่า

seconds = CONVERT_DAYS_TO_SECONDS(num_days)

สิ่งต่าง ๆ หยุดชัดเจนเมื่อคุณเหนื่อยมาก รหัส defensively


13
การเข้าสู่โหมดการกระทืบอย่างที่คุณอธิบายเป็นปฏิปักษ์ต่อการต่อต้านที่ควรหลีกเลี่ยง โปรแกรมเมอร์สามารถเข้าถึงผลผลิตสูงสุดอย่างยั่งยืนได้ที่ประมาณ 35-40 ชั่วโมง / สัปดาห์
btilly

4
@billy ฉันเห็นด้วยอย่างสุดใจกับคุณ แต่มันเกิดขึ้นบ่อยครั้งเนื่องจากปัจจัยภายนอก
Vitor Py

3
ฉันกำหนดค่าคงที่ทั่วไปเป็นวินาทีวินาทีวันและชั่วโมง หากไม่มีสิ่งใดที่ '30 * MINUTE 'เป็นเรื่องง่ายที่จะอ่านและฉันรู้ว่ามันถึงเวลาแล้วโดยไม่ต้องคิดถึงมัน
Zachary K

9
@btilly: ค่าสูงสุดคือ 35-40 ชั่วโมงหรือระดับ alchohol ในเลือดระหว่าง 0.129% ถึง 0.138% ฉันอ่านมันบนXKCDดังนั้นมันจะต้องเป็นจริง!
oosterwal

1
หากฉันเห็นค่าคงที่เช่น HOURS_PER_DAY ฉันจะลบออกและทำให้เสียชื่อเสียงต่อหน้าเพื่อนร่วมงานของคุณ ตกลงบางทีฉันอาจจะทิ้งความอัปยศอดสูสาธารณะ แต่ฉันอาจจะลบมัน
Ed S.

8

เวลาที่จะบอกว่าไม่เป็นเกือบตลอดเวลา เวลาที่ฉันพบว่าการใช้หมายเลขที่กำหนดรหัสยากอยู่ในสถานที่เช่นโครงร่าง UI - การสร้างค่าคงที่สำหรับการวางตำแหน่งของตัวควบคุมทุกตัวในแบบฟอร์มจะได้รับมากและเหนื่อยล้าและถ้ารหัสนั้นจัดการโดยนักออกแบบ UI ไม่สำคัญมาก ... เว้นแต่ว่า UI นั้นถูกวางแบบไดนามิกหรือใช้ตำแหน่งสัมพัทธ์กับจุดยึดหรือเขียนด้วยมือ ในกรณีนี้ฉันจะบอกว่าดีกว่าที่จะกำหนดค่าคงที่ที่มีความหมายสำหรับเค้าโครง และถ้าคุณต้องการปัจจัยเหลวไหลที่นี่หรือที่นั่นเพื่อจัดตำแหน่ง / บางสิ่งบางอย่าง "ถูกต้อง" ก็ควรที่จะกำหนด

แต่ในตัวอย่างของคุณฉันคิดว่าการแทนที่24 * 60 * 60ด้วยDAYS_TO_SECONDS_FACTORดีกว่า


ฉันยอมรับว่าค่าที่กำหนดตายตัวก็ใช้ได้เช่นกันเมื่อบริบทและการใช้งานนั้นชัดเจนอย่างสมบูรณ์ อย่างไรก็ตามนี่คือการตัดสินการเรียก ...

ตัวอย่าง:

ตามที่ @rmx ชี้ให้เห็นการใช้ 0 หรือ 1 เพื่อตรวจสอบว่ารายการว่างเปล่าหรืออาจเป็นขอบเขตของลูปเป็นตัวอย่างของกรณีที่วัตถุประสงค์ของค่าคงที่นั้นชัดเจนมาก


2
ปกติแล้วมันก็โอเคที่จะใช้0หรือ1ฉันคิดว่า จะดีกว่าif(someList.Count != 0) ... if(someList.Count != MinListCount) ...ไม่เสมอไป แต่โดยทั่วไปแล้ว
ไม่มีใคร

2
@Dima: VS ผู้ออกแบบฟอร์มจัดการทุกอย่าง ถ้ามันต้องการสร้างค่าคงที่นั่นก็ดีสำหรับฉัน แต่ฉันจะไม่เข้าไปในรหัสที่สร้างขึ้นและแทนที่ค่าตายตัวทั้งหมดด้วยค่าคงที่
FrustratedWithFormsDesigner

4
อย่าสับสนรหัสหมายถึงการสร้างและจัดการโดยเครื่องมือที่มีรหัสที่เขียนขึ้นเพื่อการบริโภคของมนุษย์
biziclop

1
@FrustratedWithFormsDesigner เป็น @biziclop ชี้ให้เห็นว่ารหัสที่สร้างเป็นสัตว์ที่แตกต่างอย่างสิ้นเชิง ค่าคงที่ที่มีชื่อจะต้องใช้ในรหัสที่ผู้ใช้อ่านและแก้ไข รหัสที่สร้างอย่างน้อยในกรณีอุดมคติไม่ควรแก้ไขเลย
Dima

2
@FrustratedWithFormsDesigner: จะเกิดอะไรขึ้นถ้าคุณมีฮาร์ดโค้ดที่รู้จักกันดีในหลาย ๆ ไฟล์ในโปรแกรมของคุณที่จู่ๆต้องเปลี่ยน? ตัวอย่างเช่นคุณเขียนโค้ดค่าที่แทนจำนวนนาฬิกาขีดต่อไมโครวินาทีสำหรับตัวประมวลผลแบบฝังตัวจากนั้นจะถูกบอกให้ย้ายซอฟต์แวร์ของคุณไปยังการออกแบบที่มีจำนวนสัญญาณนาฬิกาติ๊กต่อไมโครวินาที หากค่าของคุณเป็นสิ่งที่พบได้ทั่วไปเช่น 8 การค้นหา / แทนที่ไฟล์จำนวนมากอาจทำให้เกิดปัญหามากขึ้น
oosterwal

8

หยุดเมื่อคุณไม่สามารถปักความหมายหรือจุดประสงค์ลงในตัวเลข

seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE

อ่านง่ายกว่าแค่ใช้ตัวเลข (แม้ว่าจะสามารถทำให้อ่านง่ายขึ้นโดยมีSECONDS_PER_DAYค่าคงที่เดียวแต่เป็นปัญหาที่แยกจากกันโดยสิ้นเชิง)

สมมติว่านักพัฒนาดูรหัสสามารถดูว่าทำอะไร แต่อย่าคิดว่าพวกเขารู้ว่าทำไม หากค่าคงที่ของคุณช่วยให้เข้าใจสาเหตุไปด้วย ถ้าไม่ทำไม่ได้

หากคุณมีค่าคงที่มากเกินไปตามที่แนะนำโดยหนึ่งคำตอบให้ลองใช้ไฟล์การกำหนดค่าภายนอกแทนเนื่องจากการมีค่าคงที่หลายสิบในไฟล์นั้นไม่ช่วยให้อ่านง่ายขึ้น


7

ฉันอาจจะพูดว่า "ไม่" กับสิ่งที่ชอบ:

#define HTML_END_TAG "</html>"

และแน่นอนจะพูดว่า "ไม่" ถึง:

#define QUADRATIC_DISCRIMINANT_COEF 4
#define QUADRATIC_DENOMINATOR_COEF  2

7

หนึ่งในตัวอย่างที่ดีที่สุดที่ฉันได้พบเพื่อส่งเสริมการใช้ค่าคงที่สำหรับสิ่งที่ชัดเจนเช่นHOURS_PER_DAY:

เราคำนวณว่านานแค่ไหนที่นั่งอยู่ในคิวงานของบุคคล ข้อกำหนดถูกกำหนดไว้อย่างหลวม ๆ และโปรแกรมเมอร์เขียนโค้ดยาก24ในหลาย ๆ ที่ ในที่สุดเราก็ตระหนักว่ามันไม่ยุติธรรมที่จะลงโทษผู้ใช้ที่ต้องเผชิญกับปัญหาเป็นเวลา 24 ชั่วโมงเมื่อทำงานได้จริงเพียง 8 ชั่วโมงต่อวัน เมื่องานมาเพื่อแก้ไขปัญหานี้และดูว่ารายงานอื่นใดที่อาจมีปัญหาเดียวกันมันค่อนข้างยากที่จะ grep / ค้นหาผ่านรหัส 24 จะง่ายกว่ามากในการ grep / ค้นหาHOURS_PER_DAY


โอ้โหดังนั้นชั่วโมงต่อวันจะแตกต่างกันไปตามว่าคุณอ้างถึงชั่วโมงทำงานต่อวัน (ฉันทำงาน 7.5 BTW) หรือชั่วโมงต่อวัน ในการเปลี่ยนความหมายของค่าคงที่เช่นนั้นคุณต้องการแทนที่ชื่อของมันเป็นอย่างอื่น ประเด็นของคุณเกี่ยวกับการค้นหานั้นทำได้ง่ายนั้นถูกต้องแล้ว
gbjbaanb

2
ปรากฏว่าในกรณีนี้ HOURS_PER_DAY ไม่ใช่ค่าคงที่ที่ต้องการจริงๆ แต่ความสามารถในการค้นหาโดยใช้ชื่อเป็นประโยชน์อย่างมากแม้ว่า (หรือโดยเฉพาะถ้า) คุณต้องเปลี่ยนเป็นอย่างอื่นในหลาย ๆ ที่
David K

4

ฉันคิดว่าตราบใดที่จำนวนนั้นคงที่และไม่มีความเป็นไปได้ที่จะเปลี่ยนมันก็ยอมรับได้อย่างสมบูรณ์ ดังนั้นในกรณีของคุณseconds = num_days * 24 * 60 * 60เป็นเรื่องปกติ (สมมติว่าแน่นอนว่าคุณไม่ได้ทำสิ่งที่โง่เหมือนทำเรียงลำดับของการคำนวณนี้ภายในวง) seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTEและเนื้อหาที่ดีกว่าสำหรับการอ่านมากกว่า

เมื่อคุณทำสิ่งนี้สิ่งที่ไม่ดี:

lineOffset += 24; // 24 lines to a page

แม้ว่าคุณจะไม่สามารถใส่บรรทัดในหน้าได้อีกต่อไปหรือแม้ว่าคุณจะไม่ต้องการเปลี่ยนก็ตามให้ใช้ตัวแปรคงที่แทนเพราะวันหนึ่งมันจะกลับมาหลอกหลอนคุณ ในท้ายที่สุดประเด็นคือความสามารถในการอ่านไม่บันทึก 2 รอบของการคำนวณบน CPU นี่ไม่ใช่ 1978 อีกต่อไปเมื่อไบต์อันมีค่าถูกบีบเพื่อความคุ้มค่าทั้งหมด


2
คุณจะพบว่าเป็นที่ยอมรับได้หรือไม่ที่จะใช้รหัสที่ไม่มีการเปลี่ยนแปลงค่า 86400 แทนการใช้ค่าคงที่ SECONDS_PER_DAY คุณจะยืนยันได้อย่างไรว่าการเกิดขึ้นของค่าทั้งหมดนั้นถูกต้องและไม่พลาด 0 หรือการสลับ 6 ad 4 เป็นต้น
oosterwal

ถ้าเช่นนั้นทำไมไม่: วินาที = num_days * 86400 ที่จะไม่เปลี่ยนแปลงอย่างใดอย่างหนึ่ง
JeffO

2
ฉันทำ SECONDS_PER_DAY ทั้งสองวิธีแล้ว - ใช้ชื่อและใช้หมายเลข เมื่อคุณกลับมาที่รหัส 2 ปีต่อมาหมายเลขที่มีชื่อจะเหมาะสมกว่าเสมอ
quick_now

2
วินาที = num_days * 86400 ไม่ชัดเจนสำหรับฉัน นั่นคือสิ่งที่สำคัญที่สุด ถ้าฉันเห็น "seconds = num_days * 24 * 60 * 60" นอกเหนือจากความจริงที่ว่าชื่อตัวแปรให้ความหมายค่อนข้างดีในกรณีนี้ฉันจะถามตัวเองทันทีว่าทำไมฉันถึงแยกพวกเขาออกและความหมายชัดเจนเพราะฉันทิ้ง พวกเขาเป็นตัวเลข (ซึ่งเป็นค่าคงที่) ไม่ใช่ตัวแปรที่จะต้องมีการตรวจสอบเพิ่มเติมเพื่อทำความเข้าใจค่านิยมของพวกเขาและหากค่าคงที่
Neil

1
สิ่งที่ผู้คนมักจะไม่เข้าใจ: หากคุณเปลี่ยนค่า lineOffset จาก 24 เป็น 25 คุณจะต้องผ่านรหัสทั้งหมดของคุณเพื่อดูว่ามีการใช้ 24 รหัสใดและต้องการเปลี่ยนแปลงจากนั้นคำนวณทุกวันเป็นชั่วโมง คูณด้วย 24 จริงๆได้รับในทางของคุณ
gnasher729

3
seconds = num_days * 24 * 60 * 60

เป็นสิ่งที่ดีอย่างสมบูรณ์ ตัวเลขเหล่านี้ไม่ได้มีความมหัศจรรย์อย่างที่พวกเขาจะไม่เปลี่ยนแปลง

ตัวเลขใด ๆ ที่สามารถเปลี่ยนแปลงได้อย่างสมเหตุสมผลหรือไม่มีความหมายที่ชัดเจนควรจะใส่ไว้ในตัวแปร ซึ่งหมายความว่าพวกเขาทั้งหมดสวยมาก


2
จะseconds = num_days * 86400ยังคงเป็นที่ยอมรับหรือไม่ หากค่าเช่นนั้นมีการใช้หลายครั้งในหลาย ๆ ไฟล์คุณจะยืนยันได้อย่างไรว่ามีบางคนที่ไม่ได้พิมพ์โดยบังเอิญseconds = num_days * 84600ในที่เดียวหรือสองแห่ง
oosterwal

1
การเขียน 86400 นั้นแตกต่างจากการเขียน 24 * 60 * 60
Carra

4
แน่นอนมันจะเปลี่ยน ไม่ใช่ทุกวันมี 86,400 วินาทีในนั้น พิจารณาตัวอย่างเช่นเวลาออมแสง ปีละครั้งบางสถานที่มีเพียง 23 ชั่วโมงในหนึ่งวันและอีกวันหนึ่งพวกเขาจะมี 25 กะเทยตัวเลขของคุณจะเสีย
เดฟ DeLong

1
@ เดฟจุดที่ยอดเยี่ยม มี Leap วินาทีอยู่ - en.wikipedia.org/wiki/Leap_second

จุดยุติธรรม การเพิ่มฟังก์ชั่นจะเป็นการป้องกันหากคุณจำเป็นต้องตรวจจับข้อยกเว้นเหล่านั้น
Carra

3

ฉันจะหลีกเลี่ยงการสร้างค่าคงที่ (ค่าเวทมนต์) เพื่อแปลงค่าจากหน่วยหนึ่งเป็นหน่วยอื่น ในกรณีของการแปลงฉันชอบชื่อวิธีการพูด ในตัวอย่างนี้สิ่งนี้จะเป็นเช่นDayToSeconds(num_days)ภายในวิธีที่ไม่ต้องการค่าเวทย์มนตร์เพราะความหมายของ "24" และ "60" นั้นชัดเจน

ในกรณีนี้ฉันไม่เคยใช้วินาที / นาที / ชั่วโมง ฉันจะใช้ TimeSpan / DateTime เท่านั้น


1

ใช้บริบทเป็นพารามิเตอร์ในการตัดสินใจ

ตัวอย่างเช่นคุณมีฟังก์ชั่นที่เรียกว่า "calculSecondsBetween: aDay และ: anotherDay" คุณไม่จำเป็นต้องทำการ exaplanation มากนักเกี่ยวกับตัวเลขเหล่านั้นเนื่องจากชื่อฟังก์ชั่นเป็นตัวแทนที่ค่อนข้างมาก

และคำถามอื่นคือความเป็นไปได้ในการคำนวณในวิธีที่แตกต่างกันอย่างไร บางครั้งมีหลายวิธีที่จะทำสิ่งเดียวกันดังนั้นเพื่อแนะนำโปรแกรมเมอร์ในอนาคตและแสดงวิธีที่คุณใช้การกำหนดค่าคงที่สามารถช่วยให้เข้าใจได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.