マジックナンバーの削除:「いいえ」と言うタイミングはいつですか?


36

マジックナンバー(ハードコードされた値)がプログラムに大混乱をもたらす可能性があることは誰もが知っています。特にコメントのないコードセクションを変更するときは、どこに線を引きますか?

たとえば、2日間の秒数を計算する関数がある場合、置き換えますか

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

数値リテラルの代わりにシンボリック定数を使用する理由は2つあります。

  1. マジックナンバーが変更された場合のメンテナンスを簡素化するため。これはあなたの例には当てはまりません。1時間の秒数や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
親愛なる、あなたは私がかつて働いた同じ男と働いたに違いない。
すぐに

2
@gablin:公平を期すと、パブが蓋をするのは非常に便利です。
アラン・ピアース

10
私はこれを見た:public static int THREE = 3;...注-いいえfinal
スティーブンC

29

私は魔法の数字を持たないというルールを守ります。

ながら

seconds = num_days * 24 * 60 * 60

クランチモードで3〜4週間、1日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」が本当に読みやすいなら、それについて考える必要はありません。
ザカリーK

9
@btilly:ピークは35〜40時間、または血中アルコール濃度は0.129%〜0.138%です。私はXKCDでそれを読んだので、それは真実でなければなりません!
oosterwal

1
HOURS_PER_DAYのような定数が見つかった場合、それを削除してから、同僚の前で公に辱めます。公共の屈辱をあきらめるかもしれませんが、おそらく削除するでしょう。
エドS.

8

ノーと言う時はほとんどいつもです。ハードコードされた数字を使用する方が簡単だと思うときは、UIレイアウトなどの場所にあります-フォーム上のすべてのコントロールの配置に定数を作成すると、非常に手間がかかり疲れます。そのコードは通常UIデザイナーによって処理されます大した問題ではありません。... UIが動的にレイアウトされる場合、またはアンカーに対する相対位置を使用する場合、または手動で作成される場合を除きます。その場合、レイアウトに意味のある定数を定義する方が良いと思います。そして、何かを「ちょうどいい」ように配置/配置するために、あちこちでファッジファクターが必要な場合は、それも定義する必要があります。

しかし、あなたの例では、置換24 * 60 * 60するDAYS_TO_SECONDS_FACTOR方が良いと思います。


コンテキストと使用法が完全に明確な場合、ハードコードされた値でも問題ないことを認めます。これは、しかし、判断の呼び出しです...

例:

@rmxが指摘したように、0または1を使用してリストが空であるか、ループの境界にあるかどうかを確認することは、定数の目的が非常に明確な場合の例です。


2
これは、通常使用してOKです01私は数えます。if(someList.Count != 0) ...よりも優れていif(someList.Count != MinListCount) ...ます。常にではありませんが、一般的に。
誰も

2
@Dima:VS Forms Designerがすべてを処理します。定数を作成する場合は、それで問題ありません。しかし、生成されたコードには触れ、すべてのハードコードされた値を定数に置き換えません
FrustratedWithFormsDesigner

4
ただし、ツールによって生成および処理されることを意図したコードを、人間が消費するために記述されたコードと混同しないようにしましょう。
-biziclop

1
@biziclopが指摘したように、@ FrustratedWithFormsDesignerは、生成されたコードはまったく異なる動物です。名前付き定数は、人が読み取り、変更するコードで絶対に使用する必要があります。生成されたコードは、少なくとも理想的な場合には、まったく変更しないでください。
ディマ

2
@FrustratedWithFormsDesigner:突然変更する必要があるプログラム内の多数のファイルにハードコードされた既知の値がある場合はどうなりますか?たとえば、組み込みプロセッサのマイクロ秒あたりのクロックティック数を表す値をハードコードすると、マイクロ秒あたりのクロックティック数が異なるデザインにソフトウェアを移植するように指示されます。値が8などの一般的なものであった場合、多数のファイルで検索/置換を実行すると、さらに多くの問題が発生する可能性があります。
oosterwal

8

番号の意味や目的を特定できない場合は停止してください。

seconds = num_days * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE

数字を使用するよりも読みやすくなっています。(単一のSECONDS_PER_DAY定数を持つことで読みやすくすることもできますが、それは完全に別の問題です。)

コードを見ている開発者がそのコードの動作を確認できると仮定します。しかし、彼らもその理由を知っていると仮定しないでください。あなたの定数がその理由を理解するのを助けてくれるなら、それを選んでください。そうでない場合は、しないでください。

1つの回答で示唆されたように、定数が多すぎる場合は、代わりに外部構成ファイルを使用することを検討してください。ファイルに数十個の定数があると、読みやすさが改善されません。


7

私はおそらく次のようなことに対して「いいえ」と言うでしょう。

#define HTML_END_TAG "</html>"

そして間違いなく「いいえ」と言うでしょう:

#define QUADRATIC_DISCRIMINANT_COEF 4
#define QUADRATIC_DENOMINATOR_COEF  2

7

次のような明らかなことのために定数の使用を促進するために見つけた最高の例の1つは、HOURS_PER_DAY次のとおりです。

私たちは、物事が人の仕事の列にどれくらい座っているかを計算していました。要件は大まかに定義され、プログラマ24は多くの場所でハードコーディングされました。最終的には、実際に1日8時間しか働かないのに、ユーザーが24時間問題を抱えていることを罰するのは公平ではないことがわかりました。タスクがこれを修正し、他のレポートに同じ問題があるかもしれないものを見ると、24のコードをgrep /検索することはかなり困難でした。HOURS_PER_DAY


ああ、1日あたりの時間は、1日あたりの労働時間(私は7.5 BTWで働いている)と1日の時間のどちらを指すかによって異なります。そのような定数の意味を変更するには、その名前を別の名前に置き換えたいと思います。ただし、検索を簡単にする点は有効です。
gbjbaanb

2
この場合、HOURS_PER_DAYも実際には望ましい定数ではなかったようです。しかし、名前で検索できることは、たとえ多くの場所でそれを別のものに変更する必要がある場合でも(または特にそうである場合でも)、大きな利点です。
デビッドK 14

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広告4がスワップしていないことをどのように確認しますか?
-oosterwal

それではなぜ秒:num_days * 86400ですか?それも変わりません。
ジェフ

2
名前と番号を使用して、SECONDS_PER_DAYの両方の方法を実行しました。2年後にコードに戻ったとき、名前付きの数字は常に意味があります。
すぐに

2
seconds = num_days * 86400は明確ではありません。それが最終的に重要なことです。「seconds = num_days * 24 * 60 * 60」と表示された場合、変数名がこのインスタンスで意味をかなりうまく貸しているという事実は別として、私はすぐに自分がそれらを分離した理由を自問します。それらは数値であるため(定数であるため)、変数を調べるために変数を調べる必要はありません。変数を調べて、それらの値と定数かどうかを理解する必要があります。
ニール

1
多くの人が気付いていないこと:lineOffsetの値を24から25に変更する場合は、すべてのコードを調べて、24が使用された場所と変更が必要かどうかを確認し、その後すべての日から時間の計算を行う必要があります24を掛けると本当に邪魔になります。
gnasher729

3
seconds = num_days * 24 * 60 * 60

まったく問題ありません。これらは変更されることはないため、実際には魔法の数字ではありません。

合理的に変更できる、または明確な意味を持たない数値は、変数に入れる必要があります。これはほとんどすべてを意味します。


2
それseconds = num_days * 86400でも受け入れられますか?そのような値が多くの異なるファイルで複数回使用された場合、誰かが誤っseconds = num_days * 84600て1か2か所に入力しなかったことをどのように確認しますか?
oosterwal

1
86400を書くことは24 * 60 * 60を書くこととは非常に異なります
。-カーラ

4
もちろん変わります。毎日86,400秒というわけではありません。たとえば、夏時間を考慮してください。1年に1度、一部の場所では1日で23時間しかありませんが、別の日には25 時間になります。
デイブデロング

1
@Dave、素晴らしい点。うるう秒が存在する- en.wikipedia.org/wiki/Leap_secondを

公正なポイント。これらの例外をキャッチする必要がある場合、関数を追加することは安全策です。
カーラ

3

ある単位から別の単位に値を変換するための定数(マジック値)を作成することは避けます。変換する場合、話すメソッド名を好みます。この例では、これはたとえばDayToSeconds(num_days)内部的なものになります。「24」と「60」の意味が明確であるため、メソッドはマジック値を必要としません。

この場合、秒/分/時間を使用することはありません。TimeSpan / DateTimeのみを使用します。


1

決定するパラメータとしてコンテキストを使用します

たとえば、「calculateSecondsBetween:aDay and:anotherDay」という関数がある場合、関数名が非常に代表的であるため、これらの数値が何をするかについてあまり説明する必要はありません。

そして別の質問は、それを異なる方法で計算する可能性はどれですか?同じことをする方法がたくさんあることがあるので、将来のプログラマーをガイドし、使用した方法を示すために、定数を定義することでそれを理解することができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.