アプリケーションに値をハードコーディングすることは良い考えですか?


45

アプリケーションに値をハードコーディングすることは良い考えですか?または、これらのタイプの値を変更する必要がある場合に動的に呼び出すことは常に正しいことですか?


2
構成パラメーターが役立ちます
-Gopi

53
の値がいつpi変更されるかわからない
...-Gabe

12
男、@ gabeのような人がこれが「ルール」である理由だと思う。コード内の20か所で3.14を繰り返して、実際にもっと正確なものが必要であることに気付いた場合は、めちゃくちゃです。これが明らかではないことに気づきませんでした。
ビルK

17
@Bill、それは少し失礼だった。@Gabeは明らかに冗談を言っていましたが、それ以外は、複数の場所で定数と繰り返しのマジックナンバーを使用するのではなく、設定パラメーターとハードコーディングについての質問でした。
デビッドコンラッド

1
はい、ハードコーディングは時々良いアイデアです。「Softcoding」アンチパターンに関するウィキペディアの記事を参照してください。
-user16764

回答:


64

はい、しかしそれを明らかにしてください

行う:

しないでください:


44
どちらがきれいで、diameter = 2 * radiusまたはdiameter = RADIUS_TO_DIAMETER_FACTOR * radius?実際、マジックナンバーがより良い解決策となる可能性のあるコーナーケースがあります。
ジョナスプラッカ

5
私はこの答えに十分同意できません。私はプログラミングを小説家のように考える傾向があります。あなたはコードを通してあなたの物語を伝えます、そして、人々がロジックを理解できないならば、それはあなたのコードを私の意見で価値がないものにします。それが、よく考えられた命名規則が本質的に読みやすさのためである理由です。また、マジックナンバーを使用する正当な理由はありません。マジックナンバーを使用することで、方程式から「なぜ」を削除し、理解しにくくします。たとえば、「diameter = 2 * radius」の2つは何ですか?この「diameter = RADIUS_TO_DIAMETER_FACTOR * radius」は、はるかに理にかなっています。
クリス

9
直径= 2 *半径は高校の数学からまっすぐです。「2」に名前を付けない理由は、それが他の何かの値を持つためには、物理​​学または数学、あるいはその両方の法則を変更する必要があるためです。(一方で、PiまたはPlancks定数に名前を付けることは、読みやすくするための良い動きです)。
すぐに

8
@Joonas:Pfft。本当にあなたはdiameter = radius << 1?それもあると思いますdiameter = radius << RADIUS_TO_DIAMETER_BITS_TO_SHIFT
アリ

4
どのようにいくつかのdiameter = radius.toDiameter()
カーソンマイヤーズ

26

これまでのところ、この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で私を見ないでください。言い訳しない。

おそらくいくつかのシナリオを省略しましたが、それらのほとんどをカバーしていると思います。

それで、そう、それはハードコードのものへの時々受け入れられる習慣です。ただ怠けてはいけません。単純な古いずさんなコードではなく、意識的な決定であるべきです。


良い内訳をありがとう!-ほとんどの人は、「環境設定」を追加するすべてのオプションを考慮していません-ほとんどのデータは設定ファイルまたはデータベースに配置する必要があるため、これらを避ける必要があります(ハードコードされません)。これは、MVCまたはMVVMの主力である「データとロジックを分離する」という原則に従います。文字列TestServerVar = "foo"; string ProdServerVal = "bar";
m1m1k

7

番号に識別子を割り当てる理由はさまざまです。

  • 番号が変更される可能性がある場合は、識別子が必要です。 9のすべてのインスタンスを検索して8に変更するかどうかを検討するよりもNUMBER_OF_PLANETSを見つける方がはるかに簡単です(ソフトウェアを別の言語で使用する必要がある場合は、ユーザーに表示される文字列を変更する必要があることに注意してください)事前に予測するのは難しいことです。)
  • 番号を入力するのが難しい場合。 piのような定数の場合、複数の場所で、おそらく不正確に再入力するよりも、1つの最大精度の定義を与える方が適切です。
  • 番号が異なる場所で発生する場合。 隣接する関数での45の2つの使用法を見て、それらが同じことを意味するのかと考える必要はありません。
  • 意味がすぐに認識できない場合。 誰もが3.14159265 ...が何であるかを知っていると仮定するのは安全です。すべての人が重力定数、さらにはπ/ 2を認識すると想定するのは安全ではありません。(ここでの「全員」は、ソフトウェアの性質に依存します。システムプログラマは、Unix許可ビットなどの8進表現を知っていることが期待できます。海軍/海洋アーキテクチャソフトウェアでは、提案された船体のフルード数と1.1以上であるかどうかは、作業を行うべきだれにとっても完全に自明であるかもしれません。)
  • コンテキストが認識できない場合。 1時間に60分あることは誰もが知っていますが、量が時間値またはレート値であるという即時の兆候がない場合、60で乗算または除算することは不明確な場合があります。

これにより、リテラルのハードコーディングの基準が得られます。それらは不変であり、入力するのが難しくなく、1つの場所またはコンテキストでのみ発生し、認識可能な意味を持つ必要があります。たとえば、0をARRAY_BEGINNINGとして、または1をARRAY_INCREMENTとして定義しても意味がありません。


5

他の回答への追加として。可能な場合、文字列に定数を使用します。もちろん、あなたは持ちたくない

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 {state_0 = 0、state_1 = 1、state_2 = 2、...} ...笑わないでください。濡れた魚で頭の周りの人を叩きます!
すぐに

@quicklyもちろんあなたはもっともっと何かをしたいと思うでしょうtypedef enum {init_state=0, parse_state=1, evaluation_state=2, ... }
-Earlz

6
THIS_NAMING_CONVENTION_IS_RECOMMENDED_FOR_CONSTANTS
StuperUser

4
文字列の場合、単に定数が必要なわけではありません。他の言語に簡単に変更できるように、ユーザーに見える文字列を何らかのリソースファイル(詳細はプラットフォームによって異なります)に入れたいと思います。
デビッドソーンリー

また、何らかの種類の暗号化または難読化を使用して、ビジネスロジック関連の文字列(SQLクエリなど)をリソースファイルに貼り付けることもできます。これにより、「好奇心の強い」ユーザーがロジック(またはデータベーススキーマ)をリバースエンジニアリングできなくなります。
TMN

4

それはあなたがハードコーディングと考えるものに依存します。ハードコーディングされたものをすべて回避しようとすると、最終的にはテリトリーをソフトコーディングし、作成者だけが管理できるシステムを作成します(これが究極のハードコードです)

合理的なフレームワークには多くのものがハードコーディングされており、機能します。つまり、C#アプリケーションのエントリポイントを変更できない技術的な理由はありません(静的void Main)が、ユーザーに問題を引き起こさないハードコーディング(たまにSOの質問を除く)

私が使用する経験則では、システム全体の状態に影響を与えずに変更できるもの、変更するものはすべて構成可能でなければなりません。

だから、私見、決して変わらないものをハードコーディングしないのは愚かなことです(pi、重力定数、数式の定数-球の体積を考えてください)。

また、インスタンスにプログラミングを必要とするシステムに影響を与えるものやプロセスをハードコーディングしないことは愚かなことです。つまり、追加フィールドにメンテナンス開発者が必要な場合、ユーザーがフォームに動的フィールドを追加できるようにするのは無駄です入って、それを機能させるスクリプトを書きます。また、いくつかの構成ツールを作成することは愚かです(そして、私はそれを企業環境で数回見ました)。したがって、ハードコーディングされたものは何もありませんが、IT部門の開発者のみがそれを使用でき、 Visual Studioで実行します。

結論として、物をハードコードするかどうかは、2つの変数の関数です。

  • 値は変わりますか
  • 値の変更がシステムにどのように影響するか

4

アプリケーションに値をハードコーディングすることは良い考えですか?

値が仕様指定されている場合(仕様の最終リリース)にのみ値をハードコーディングします。たとえば、HTTP OK応答は常に(RFCで変更されない限り)であるため、(私のコードの一部で)のような定数:200

public static final int HTTP_OK = 200;

それ以外の場合は、定数をプロパティファイルに保存します。

私が仕様を指定した理由は、仕様の定数を変更するには変更管理が必要であり、そこでは利害関係者が変更をレビューして承認/不承認にするからです。一晩で起こることはなく、承認に数か月/年かかります。多くの開発者が仕様(HTTPなど)を使用していることを忘れないでください。したがって、変更すると数百万のシステムが破壊されます。


3
  • 値が変化する可能性があり、実際に変化する可能性がある場合は、関与する努力が期待されるリターンを超えない限り、可能な限りソフトコーディングします
  • 一部の値ソフトコーディングできません。それらの(まれな)ケースではジョナサンのガイドラインに従う

2

コードからデータを抽出できるときはいつでも、残っているものが改善されることに気付きました。新しいリファクタリングに注目し、コードのセクション全体を改善し始めます。

定数の抽出に向けて作業するのは良い考えです。それをバカなルールとは考えずに、より良いコーディングの機会と考えてください。

最大の利点は、コードのグループで唯一の違いである類似の定数を見つける方法です。それらを配列に抽出することで、一部のファイルをそのサイズの90%削減し、その間にかなりの数のコピー&ペーストバグを修正できました。

データを抽出しないことの利点はまだありません。


2

最近、2つのlat / longペア間の距離を適切に計算するMySQL関数をコーディングしました。ピタゴラスだけを行うことはできません。緯度が極に近づくにつれて経度線が近くなります。そのため、やや毛深いトリグが関係しています。ポイントは、地球の半径をマイル単位で表す値をハードコードするかどうかについてかなり悩んでいたことです。

実際のところ、緯度/経度の線は月などで非常に接近しているにもかかわらず、私はそれをやってしまいました。そして、私の機能は木星上の点間の距離を大幅に過小報告します。私は、地球外の場所が入力されるように構築しているWebサイトのオッズはかなりスリムであると考えました。


はい、おそらく、しかしgoogle.com/moonは
ですか?Residuum

1

まあ、それはあなたの言語がコンパイルされているかどうかに依存します。コンパイルされていなくても大した問題ではありません。たとえプログラマーでない人にとっては微妙であっても、ソースコードを編集するだけです。

コンパイルされた言語でプログラミングしている場合、これは明らかに良いアイデアではありません。変数が変更された場合、再コンパイルする必要があるためです。

変数を動的に変更するためにスライダーやインターフェイスを作成する必要はありませんが、できることはテキストファイルだけです。

たとえば、私のogreプロジェクトでは、ConfigFileクラスを使用して、構成ファイルに書き込んだ変数をロードしています。


1

定数が(少なくとも私の意見では)OKである2つの場合:

  1. 何にも関連しない定数。必要なときにいつでもこれらの定数を変更でき、他に何も変更する必要はありません。例:グリッド列のデフォルトの幅。

  2. 「週あたりの日数」のような絶対に不変で、正確で、明白な定数。定数にdays = weeks * 7置き換え7ても、DAYS_PER_WEEKほとんど価値がありません。


0

私はジョナサンに完全に同意しますが、すべてのルールには例外があります...

「仕様のマジックナンバー:コードのマジックナンバー」

基本的に、記述コンテキストを取得しようとする合理的な試みの後に仕様に残っているマジックナンバーは、コードにそのまま反映されるべきであると述べています。コード内にマジックナンバーが残っている場合は、それらを分離し、それらを元のポイントに明確にリンクさせるためにあらゆる努力を払う必要があります。

データベースからマップされた値をメッセージに取り込む必要があるインターフェース契約をいくつか実行しました。ほとんどの場合、マッピングはかなり単純で、ジョナサンの一般的なガイドラインに適合しますが、ターゲットメッセージの構造が単純でひどい場合があります。構造に渡さなければならなかった値の80%以上は、遠方のシステムの仕様によって強制された定数でした。これは、メッセージ構造が巨大であるという事実と相まって、そのような定数の多くを設定する必要がありました。ほとんどの場合、「ここにMを置く」または「ここに4.10.53.10100.889450.4452を置く」と言っただけで、意味も理由も提供しませんでした。私は、それらすべての隣にコメントを入れようとしませんでした。

とはいえ、考えてみると...それはほとんどすべてを明らかにすることです ...


0

地球の重力定数の値をハードコーディングしている場合、誰も気にしません。プロキシサーバーのIPアドレスをハードコーディングすると、問題が発生します。


1
地球の重力定数により高い精度が必要な場合があるため、それを数回ハードコーディングすると問題が発生する可能性があります。
-user281377

1
ピーター・ヌーン?ハーマンの隠者から?
デビッドコンラッド

地球の重力加速度は、ほとんどの緯度と高度でほぼ9.81 m / s ^ 2です(もちろん、地下の石油を探している場合や、北極上でICBMを撃っている場合は、重力の変化が非常に重要です)他の惑星の重力加速度は異なる数値ですが、私が知る限り、重力定数は宇宙全体で一定です。gが可変の場合、変更しなければならない物理学がたくさんあります。
タングレナ

0

ほとんどありませんが、ハードコーディングされた値の複製を開始するときに最も問題が発生することに注意してください。複製しない(たとえば、クラスの実装で1回だけ使用する)場合、定数を使用しなくてもかまいません。

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