매직 넘버 제거 : 언제 "아니오"라고 말할 때입니까?


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
윤초는 어떻습니까?
John

답변:


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!
Stephen C

29

마법의 숫자를 절대로 사용하지 않는 규칙을 지키겠습니다.

동안

seconds = num_days * 24 * 60 * 60

크런치 모드에서 3-4 주 동안 하루 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)

피곤할 때 상황이 분명해지지 않습니다. 방어 적으로 코드를 작성하십시오 .


13
설명하는 것처럼 크런치 모드로 들어가는 것은 피해야하는 반 생산적인 반 패턴입니다. 프로그래머는 일주일에 약 35-40 시간 지속되는 최고 생산성에 도달합니다.
btilly

4
@btilly 나는 진심으로 당신에게 동의합니다. 그러나 종종 외부 요인으로 인해 발생합니다.
Vitor Py

3
나는 일반적으로 초, 분, 일 및 시간에 대한 상수를 정의합니다. 다른 '30 * MINUTE '가 실제로 읽기 쉽지 않으며 그것에 대해 생각할 필요없이 그 시간을 알고 있습니다.
Zachary K

9
@btilly : 피크는 35-40 시간 또는 혈중 알코올 농도가 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
@biziclop이 지적했듯이 @FrustratedWithFormsDesigner는 생성 된 코드가 완전히 다른 동물입니다. 명명 된 상수는 사람들이 읽고 수정 한 코드에 반드시 사용되어야합니다. 적어도 이상적인 경우에 생성 된 코드는 전혀 수정해서는 안됩니다.
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는 여러 곳에서 하드 코딩 되었습니다. 결국 우리는 하루에 8 시간 만 일하는 24 시간 동안 문제에 앉아있는 사용자를 처벌하는 것이 불공평하다는 것을 깨달았습니다. 작업 이이 문제를 해결하고 동일한 문제가있는 다른 보고서를 볼 때 24 코드를 통해 grep / search하기가 어려웠습니다.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

더 이상 페이지에 줄을 넣을 수 없거나 변경하려는 의도가 없더라도 언젠가 다시 귀찮게하기 때문에 상수 변수를 사용하십시오. 궁극적으로 요점은 가독성이며 CPU에 2주기의 계산을 저장하지 않습니다. 귀중한 바이트가 모든 가치를 위해 압착되었을 때 이것은 더 이상 1978 년이 아닙니다.


2
명명 된 상수 SECONDS_PER_DAY를 사용하는 대신 변경되지 않은 값 86400을 하드 코딩하는 것이 허용됩니까? 예를 들어, 값의 모든 발생이 올 바르고 0이 없거나 6 ad 4를 바꾸지 않는지 어떻게 확인할 수 있습니까?
oosterwal

그렇다면 왜 : 초 = num_days * 86400? 그것은 변하지 않을 것입니다.
JeffO

2
이름과 숫자를 사용하여 SECONDS_PER_DAY 일을 두 가지 방법으로 수행했습니다. 2 년 후 코드로 돌아 오면 이름이 지정된 번호가 항상 더 의미가 있습니다.
quick_now

2
초 = num_days * 86400은 명확하지 않습니다. 그것이 궁극적으로 중요합니다. "초 = 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해야 당신의 번호가 끊어집니다.
Dave DeLong

1
@ 데이브, 훌륭한 지적. 윤초가 존재 - en.wikipedia.org/wiki/Leap_second을

페어 포인트. 이러한 예외를 포착해야 할 경우 기능을 추가하면 안전합니다.
Carra

3

한 단위에서 다른 단위로 값을 변환하기 위해 상수 (마법 값)를 작성하지 마십시오. 변환하는 경우 말하기 방법 이름을 선호합니다. 이 예제에서는 DayToSeconds(num_days)"24"와 "60"의 의미가 명확하기 때문에이 방법은 예를 들어 내부적으로 분석법에 매직 값이 필요하지 않습니다.

이 경우에는 절대 초 / 분 / 시간을 사용하지 않습니다. TimeSpan / DateTime 만 사용합니다.


1

컨텍스트를 매개 변수로 사용하여 결정

예를 들어, "calculateSecondsBetween : aDay and : anotherDay"라는 함수가 있으면 함수 이름이 상당히 대표적이므로 해당 숫자의 기능에 대해 많은 설명을 할 필요가 없습니다.

또 다른 질문은 다른 방법으로 계산할 수있는 가능성은 무엇입니까? 때로는 똑같은 일을하는 많은 방법이 있으므로 미래의 프로그래머를 안내하고 사용하는 방법을 보여주기 위해 상수를 정의하면 도움이 될 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.