アプリケーションに値をハードコーディングすることは良い考えですか?または、これらのタイプの値を変更する必要がある場合に動的に呼び出すことは常に正しいことですか?
pi
変更されるかわからない
アプリケーションに値をハードコーディングすることは良い考えですか?または、これらのタイプの値を変更する必要がある場合に動的に呼び出すことは常に正しいことですか?
pi
変更されるかわからない
回答:
diameter = 2 * radius
またはdiameter = RADIUS_TO_DIAMETER_FACTOR * radius
?実際、マジックナンバーがより良い解決策となる可能性のあるコーナーケースがあります。
diameter = radius << 1
?それもあると思いますdiameter = radius << RADIUS_TO_DIAMETER_BITS_TO_SHIFT
。
diameter = radius.toDiameter()
これまでのところ、このQ&Aについて奇妙に感じているのは、実際に「ハードコード」や、さらに重要な代替案を明確に定義しようとした人はいないということです。
tl; dr:はい、値をハードコードすることは時々良い考えですが、いつのかに関して簡単なルールはありません; コンテキストに完全に依存します。
質問は値に絞り込みますが、これはマジックナンバーを意味しますが、それらが良いアイデアであるかどうかの答えは、実際に使用されているものに関連しています!
「ハードコードされた」値のいくつかの例は次のとおりです。
設定値
私のような声明を見るたびにしつこいcommand.Timeout = 600
です。なぜ600?誰が決めたの?それは以前にタイムアウトになり、誰かが根本的なパフォーマンスの問題を修正する代わりにハックとしてタイムアウトを上げましたか?それとも、実際に処理時間に対する何らかの既知の文書化された期待ですか?
これらはマジックナンバーや定数であってはならず、最適な値はアプリケーションが実行されている環境によって大部分または完全に決定されるため、意味のある名前を持つ構成ファイルまたはデータベースに外部化する必要があります。
数式
数式は通常かなり静的な傾向があるため、内部の定数値の性質はそれほど重要ではありません。ピラミッドの体積は(1/3)b * hです。1または3がどこから来たのか気にしますか?あんまり。以前のコメント者diameter = radius * 2
は、おそらくそれはおそらくよりも優れていると指摘しましたがdiameter = radius * RADIUS_TO_DIAMETER_CONVERSION_FACTOR
、それは誤った二分法です。
このタイプのシナリオで行うべきことは、関数を作成することです。どのように数式を思いついたのかを知る必要はありませんが、それが何のためにあるのかを知る必要があります。上記のナンセンスの代わりにvolume = GetVolumeOfPyramid(base, height)
、突然すべてがより明確になった場合、関数内にマジックナンバー(return base * height / 3
)を含めることは完全に問題ありません。
ここで重要なことは、もちろん短くてシンプルな機能を持つことです。これは、10個の引数と30行の計算を持つ関数では機能しません。その場合は、関数の構成または定数を使用してください。
ドメイン/ビジネスルール
これは、値が正確に何であるかに依存するため、常に灰色の領域です。 ほとんどの場合、定数に変換する候補となるのはこれらの特定のマジック番号です。これは、プログラムロジックを複雑にすることなくプログラムを理解しやすくするためです。テストとのif Age < 19
比較if Age < LegalDrinkingAge
。おそらく定数なしで何が起こっているのかを理解できるでしょうが、説明的なタイトルの方が簡単です。
これらは、たとえば、関数の抽象化の候補にもなりfunction isLegalDrinkingAge(age) { return age >= 19 }
ます。唯一のことは、多くの場合、ビジネスロジックはそれよりもはるかに複雑であり、それぞれ20〜30個のパラメーターを持つ多数の関数を書き始めるのは意味がないかもしれません。オブジェクトや関数に基づく明確な抽象化がなければ、定数に頼ることは問題ありません。
警告は、税務部門で働いている場合、書くのは本当に、非常に面倒で、正直無意味になりますAttachForm(FORM_CODE_FOR_SINGLE_TAXPAYER_FILING_JOINTLY_FOR_DEPRECIATION_ON_ARMPIT_HAIR)
。あなたはそれをするつもりはありません。AttachForm("B-46")
そこで働いたことのある開発者は、「B-46」が単一の納税者が何とか何とか提出するためのフォームコードであることを知っているからです。フォームコードはドメイン自体の一部であり、変更されることはないため、実際にはマジックナンバーではありません。
そのため、ビジネスロジックでは定数を控えめに使用する必要があります。基本的に、その「マジックナンバー」が実際にマジックナンバーであるかどうか、またはドメインのよく知られた側面であるかどうかを理解する必要があります。ドメインである場合、変更される可能性が非常に高い場合を除いて、ソフトコーディングしないでください。
エラーコードとステータスフラグ
これらをハードコードすることは決して許されPrevious action failed due to error code 46
ません。言語がサポートしている場合、列挙型を使用する必要があります。それ以外の場合、通常は特定のエラータイプの有効な値を指定する定数でいっぱいのファイル/モジュール全体があります。
return 42
エラーハンドラー、capicheで私を見ないでください。言い訳しない。
おそらくいくつかのシナリオを省略しましたが、それらのほとんどをカバーしていると思います。
それで、そう、それはハードコードのものへの時々受け入れられる習慣です。ただ怠けてはいけません。単純な古いずさんなコードではなく、意識的な決定であるべきです。
番号に識別子を割り当てる理由はさまざまです。
これにより、リテラルのハードコーディングの基準が得られます。それらは不変であり、入力するのが難しくなく、1つの場所またはコンテキストでのみ発生し、認識可能な意味を持つ必要があります。たとえば、0をARRAY_BEGINNINGとして、または1をARRAY_INCREMENTとして定義しても意味がありません。
他の回答への追加として。可能な場合、文字列に定数を使用します。もちろん、あなたは持ちたくない
const string server_var="server_var";
しかし、あなたは持っている必要があります
const string MySelectQuery="select * from mytable;";
(実際には、常に特定のテーブルからすべての結果を取得するクエリがあると仮定します)
それ以外は、0以外の任意の数の定数を使用します(通常)。255の許可ビットマスクが必要な場合は、使用しないでください
const int 8th_bit=255; //or some other obscure naming scheme that equates to 255.
代わりに使用します
const int AllowGlobalRead=255;
もちろん、定数とともに、列挙子を使用するタイミングを知っています。上記のケースはおそらく1つに収まるでしょう。
typedef enum {init_state=0, parse_state=1, evaluation_state=2, ... }
それはあなたがハードコーディングと考えるものに依存します。ハードコーディングされたものをすべて回避しようとすると、最終的にはテリトリーをソフトコーディングし、作成者だけが管理できるシステムを作成します(これが究極のハードコードです)
合理的なフレームワークには多くのものがハードコーディングされており、機能します。つまり、C#アプリケーションのエントリポイントを変更できない技術的な理由はありません(静的void Main)が、ユーザーに問題を引き起こさないハードコーディング(たまにSOの質問を除く)
私が使用する経験則では、システム全体の状態に影響を与えずに変更できるもの、変更するものはすべて構成可能でなければなりません。
だから、私見、決して変わらないものをハードコーディングしないのは愚かなことです(pi、重力定数、数式の定数-球の体積を考えてください)。
また、インスタンスにプログラミングを必要とするシステムに影響を与えるものやプロセスをハードコーディングしないことは愚かなことです。つまり、追加フィールドにメンテナンス開発者が必要な場合、ユーザーがフォームに動的フィールドを追加できるようにするのは無駄です入って、それを機能させるスクリプトを書きます。また、いくつかの構成ツールを作成することは愚かです(そして、私はそれを企業環境で数回見ました)。したがって、ハードコーディングされたものは何もありませんが、IT部門の開発者のみがそれを使用でき、 Visual Studioで実行します。
結論として、物をハードコードするかどうかは、2つの変数の関数です。
アプリケーションに値をハードコーディングすることは良い考えですか?
値が仕様で指定されている場合(仕様の最終リリース)にのみ値をハードコーディングします。たとえば、HTTP OK応答は常に(RFCで変更されない限り)であるため、(私のコードの一部で)のような定数:200
public static final int HTTP_OK = 200;
それ以外の場合は、定数をプロパティファイルに保存します。
私が仕様を指定した理由は、仕様の定数を変更するには変更管理が必要であり、そこでは利害関係者が変更をレビューして承認/不承認にするからです。一晩で起こることはなく、承認に数か月/年かかります。多くの開発者が仕様(HTTPなど)を使用していることを忘れないでください。したがって、変更すると数百万のシステムが破壊されます。
最近、2つのlat / longペア間の距離を適切に計算するMySQL関数をコーディングしました。ピタゴラスだけを行うことはできません。緯度が極に近づくにつれて経度線が近くなります。そのため、やや毛深いトリグが関係しています。ポイントは、地球の半径をマイル単位で表す値をハードコードするかどうかについてかなり悩んでいたことです。
実際のところ、緯度/経度の線は月などで非常に接近しているにもかかわらず、私はそれをやってしまいました。そして、私の機能は木星上の点間の距離を大幅に過小報告します。私は、地球外の場所が入力されるように構築しているWebサイトのオッズはかなりスリムであると考えました。
まあ、それはあなたの言語がコンパイルされているかどうかに依存します。コンパイルされていなくても大した問題ではありません。たとえプログラマーでない人にとっては微妙であっても、ソースコードを編集するだけです。
コンパイルされた言語でプログラミングしている場合、これは明らかに良いアイデアではありません。変数が変更された場合、再コンパイルする必要があるためです。
変数を動的に変更するためにスライダーやインターフェイスを作成する必要はありませんが、できることはテキストファイルだけです。
たとえば、私のogreプロジェクトでは、ConfigFileクラスを使用して、構成ファイルに書き込んだ変数をロードしています。
定数が(少なくとも私の意見では)OKである2つの場合:
何にも関連しない定数。必要なときにいつでもこれらの定数を変更でき、他に何も変更する必要はありません。例:グリッド列のデフォルトの幅。
「週あたりの日数」のような絶対に不変で、正確で、明白な定数。定数にdays = weeks * 7
置き換え7
ても、DAYS_PER_WEEK
ほとんど価値がありません。
私はジョナサンに完全に同意しますが、すべてのルールには例外があります...
「仕様のマジックナンバー:コードのマジックナンバー」
基本的に、記述コンテキストを取得しようとする合理的な試みの後に仕様に残っているマジックナンバーは、コードにそのまま反映されるべきであると述べています。コード内にマジックナンバーが残っている場合は、それらを分離し、それらを元のポイントに明確にリンクさせるためにあらゆる努力を払う必要があります。
データベースからマップされた値をメッセージに取り込む必要があるインターフェース契約をいくつか実行しました。ほとんどの場合、マッピングはかなり単純で、ジョナサンの一般的なガイドラインに適合しますが、ターゲットメッセージの構造が単純でひどい場合があります。構造に渡さなければならなかった値の80%以上は、遠方のシステムの仕様によって強制された定数でした。これは、メッセージ構造が巨大であるという事実と相まって、そのような定数の多くを設定する必要がありました。ほとんどの場合、「ここにMを置く」または「ここに4.10.53.10100.889450.4452を置く」と言っただけで、意味も理由も提供しませんでした。私は、それらすべての隣にコメントを入れようとしませんでした。
とはいえ、考えてみると...それはほとんどすべてを明らかにすることです ...
地球の重力定数の値をハードコーディングしている場合、誰も気にしません。プロキシサーバーのIPアドレスをハードコーディングすると、問題が発生します。
ほとんどありませんが、ハードコーディングされた値の複製を開始するときに最も問題が発生することに注意してください。複製しない(たとえば、クラスの実装で1回だけ使用する)場合、定数を使用しなくてもかまいません。