これは議論の余地のあるトピックであり、プログラマーの数と同じくらい多くの意見があると思います。しかし、そのために、私はビジネス(またはあなたの職場)での一般的な慣行を知りたいのです。
私の職場では、厳密なコーディングガイドラインがあります。その1つのセクションは、魔法の文字列/数字専用です。それは述べています(C#の場合):
記号定数を定義する場合以外は、コードでリテラル値(数値または文字列)を使用しないでください。次のパターンを使用して定数を定義します。
public class Whatever { public static readonly Color PapayaWhip = new Color(0xFFEFD5); public const int MaxNumberOfWheels = 18; }
例外があります。値0、1、nullはほとんど常に安全に使用できます。多くの場合、値2および-1でも問題ありません。ロギングまたはトレースを目的とした文字列は、このルールから除外されます。リテラルは、その意味が文脈から明らかであり、将来の変更の影響を受けない場合に許可されます。
mean = (a + b) / 2; // okay
WaitMilliseconds(waitTimeInSeconds * 1000); // clear enough
理想的な状況は、以下の場合にコードの可読性/保守性への影響を示すいくつかの公式調査用紙です。
私は、次のような定数を宣言するところまで行く私の同僚の1人と議論するときに、科学に基づいたいくつかの引数を持つためにこれを行いたいと思います。
private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;
別の例は次のようになります(これはJavaScriptにあります)。
var someNumericDisplay = new NumericDisplay("#Div_ID_Here");
DOM IDが1か所でしか使用されていない場合、そのIDをJavaScriptファイルの上に貼り付けますか?
次のトピックを読みました。
StackExchange
StackOverflow
バイトITコミュニティ
さらに多くの記事があり、これらを読んだ後、いくつかのパターンが現れます。
だから私の質問は、コードで魔法の文字列と数字を使用する必要がありますか?私は特に、可能であれば参考文献に裏付けられた専門家の回答を探しています。
...次のような定数を宣言するところまで行く私の同僚の一人と議論するとき:
private const char SemiColon = ';'; private const char Space = ' '; private const int NumberTen = 10;
同僚との間に行う必要がある議論は、リテラルスペースをSpace
と命名することではありませんが、定数に対する名前の選択が不十分です。
コードの仕事が、セミコロン(a;b;c
)で区切られたフィールドを含み、それ自体がスペース(a;b;c d;e;f
)で区切られているレコードのストリームを解析することであるとします。仕様を書いた人が今から1か月後に電話をかけて「私たちは間違っていた、レコードのフィールドはパイプ記号(a|b|c d|e|f
)で区切られている」と言った場合、どうしますか?
同僚が好む値としての名前のスキームでは、リテラル(SemiColon = '|'
)の値を変更し、それ以外の場合にSemiColon
を引き続き使用するコードを使用する必要があります。セミコロンはもうありません。 コードレビューの否定的なコメント につながります。それを軽減するために、リテラルの名前をPipeSymbol
に変更し、すべてのSemiColon
をPipeSymbol
に変更します。そのレートでは、最初にリテラルセミコロン(';'
)を使用しただけかもしれません。これは、使用を個別に評価する必要があり、同じ数の変更を加えることになるためです。
定数の識別子は、値が何をするかではなく、値が何をするかを説明する必要があり、それは同僚が行った場所です雑草に左折。上記のフィールド分割アプリケーションでは、セミコロンの目的はフィールドセパレータであり、定数はそれに応じて名前を付ける必要があります。
private const char FieldSeparator = ';'; // Will become '|' a month from now
private const char RecordSeparator = ' ';
private const int MaxFieldsPerRecord = 10;
このように、フィールドセパレーターが変更されると、定数の宣言である1行のコードだけが変更されます。変更を見ると、その1行だけが表示され、フィールドセパレータがセミコロンからパイプシンボルに変更されたことがすぐにわかります。定数を使用していたので変更する必要のなかった残りのコードは同じままで、読者はそれを調べて他に何が行われたかを確認する必要はありません。
セミコロンを定数として定義することは冗長です。セミコロンそれ自体はすでに定数だからです。変わることはありません。
誰かが「用語の変更、+が新しいセミコロンになりました」と発表する日とは異なり、同僚は定数を更新するために喜んでラッシュします(彼らは私を笑いました-今すぐ見てください) 。
一貫性の問題もあります。私は彼のNumberTen
定数が誰もが使用することはないことを保証します(ほとんどのコーダーは彼らの頭から離れていません)。黙示録が来て "ten"がグローバルに9に再スケールされるとき、定数を更新してもトリックは行われません。コードにリテラル10
sのヒープが残っているため、システムは完全に予測不能になります。 「10」は「9」を意味するという革命的な仮定の範囲内でもです。
すべての設定をconstとして保存することも、私が考え直したいことです。これを軽く行うべきではありません。
これまでに収集したこのタイプの使用例は何ですか?ラインターミネーター...最大再試行回数...ホイールの最大数...これらが変更されないことは確かですか
デフォルトの設定を変更するには、アプリケーションを再コンパイルする必要があり、場合によっては、その依存関係さえもコンパイルする必要があります(数値のconst値はコンパイル中にハードコードされる場合があるため)。
テストとモックの側面もあります。接続文字列をconstとして定義しましたが、ユニットテストでデータベースアクセスを模擬(偽の接続を確立)することはできません。
private const char SemiColon = ';';
private const char Space = ' ';
private const int NumberTen = 10;
したがって、あなたの同僚は毎日のWTFエントリを目指しています。これらの定義はばかげて冗長です。ただし、他の人が指摘したように、次の定義はnotは馬鹿げているか冗長です。
private const char StatementTerminator = ';';
private const char Delimiter = ' ';
private const int BalanceInquiryCode = 10;
「魔法の」数字と文字列は、直接のリテラル値を超える意味を持つ定数です。定数10
は「10のこと」(特定の操作やエラー条件のコードなど)を超える意味を持ちます。つまり、「魔法」になり、抽象的な意味を表す記号定数に置き換える必要があります。
シンボリック定数は、意図を明確に説明するだけでなく、リテラルのスペルを間違えた場合の頭痛の種も減らします。 1行のコードで "CVS"から "CSV"への単純な転置は、ユニットテストとQAをすべて通過し、本番環境に移行して、特定の操作が失敗する原因となりました。はい、明らかに単体テストとQAテストは不完全であり、それはそれ自体が問題ですが、シンボリック定数を使用すると、そのような胸焼けを完全に回避できただろう。
それについて議論の余地はないはずです。重要なのは、マジックナンバーを使用するかどうかではなく、可読コードを使用することです。if(request.StatusCode == 1)
とif(request.HasSucceeded)
の違いを考慮してください。この場合、後者の方がはるかに読みやすいと主張しますが、これは、int MaxNumberOfWheels = 18
。
PS:これが私がコーディングガイドラインを絶対に嫌う理由です。開発者は、このような判断を下せるよう十分に成熟している必要があります。彼らはそれを神が誰が知っているかによって形成したテキストに任せるべきではありません。