web-dev-qa-db-ja.com

あなたは定数をどこに置くべきですか、そしてなぜですか?

主に大規模なアプリケーションでは、通常、「定数」用の場所はわずかです。

  • GUIと内部定数(タブページのタイトル、グループボックスのタイトル、計算要素、列挙)の1つのクラス
  • データベースのテーブルと列の1つのクラス(この部分は生成されたコード)とそれらの読み取り可能な名前(手動で割り当て)
  • アプリケーションメッセージの1つのクラス(ログ、メッセージボックスなど)

定数は通常、それらのクラスで異なる構造体に分けられます。 C++アプリケーションでは、定数は.hファイルでのみ定義され、値は.cppファイルで割り当てられます。

利点の1つは、すべての文字列などが1つの中心的な場所にあり、何かを変更する必要があるときに誰もがどこにそれらを見つけるかを知っていることです。

これは特に、プロジェクトマネージャーが人が行き来するときに好きなように見えるので、アプリケーションの構造を掘り下げることなく、誰でもこのような些細なことを変更できます。

また、類似のグループボックス/タブページなどのタイトルを一度に簡単に変更できます。別の側面は、そのクラスを印刷して、キャプションが直感的であるかどうか、およびユーザーへのメッセージが詳細すぎるか、混乱しすぎるかどうかを確認できる非プログラマーに渡すことができることです。

ただし、次のような欠点があります。

  • すべての単一のクラスは定数クラスに密結合されています
  • 定数を追加/削除/名前変更/移動するには、少なくともアプリケーションの90%を再コンパイルする必要があります(注:値を変更しても、少なくともC++では必要ありません)。 1500クラスのC++プロジェクトの1つでは、これは約7分のコンパイル時間(プリコンパイル済みヘッダーを使用、それがない場合は約50分)に加えて、特定の静的ライブラリーに対して約10分のリンクを意味します。
  • Visual Studio Compilerによる速度が最適化されたリリースの構築には、最大3時間かかります。大量のクラス関係がソースであるかどうかはわかりませんが、そうである可能性もあります。
  • 何かを非常に迅速にテストし、そのテストだけのために15分(そしてその後のすべてのテスト)を待ちたくないので、一時的にコードに文字列を直接ハードコーディングしなければなりません。 「私は後で修正します」という考えがどうなるか誰もが知っています。
  • 別のプロジェクトでクラスを再利用することは必ずしも簡単ではありません(主に他の密結合が原因ですが、定数の処理では簡単にはなりません)。

そのような定数はどこに保存しますか?また、上記の利点に準拠するより優れたコンセプトがあることをプロジェクトマネージャーに納得させるために、どのような議論を持ちますか?

C++固有または独立した答えを自由に与えてください。

PS:この質問は一種の主観的なものですが、正直に言って、この種の質問については、このサイトより良い場所を知りません。

このプロジェクトの更新

コンパイル時のことに関するニュースがあります:
カレブとgbjbaanbの投稿に従って、時間があるときに定数ファイルをいくつかの他のファイルに分割しました。私は最終的にプロジェクトをいくつかのライブラリーに分割しました。これをリリースモードでコンパイルすると、データベース定義(テーブル、列名など-8000を超えるシンボル)を含み、特定のハッシュを構築する自動生成ファイルにより、リリースモードでのコンパイル時間が膨大になることがわかりました。

DB定数を含むライブラリのMSVCオプティマイザを非アクティブ化すると、プロジェクト(いくつかのアプリケーション)の合計コンパイル時間を短縮できるようになりましたリリースモードの場合最大8時間から1時間未満に!

MSVCがこれらのファイルを最適化するのに非常に苦労している理由はまだわかっていませんが、今のところこの変更により、ナイトリービルドのみに依存する必要がなくなったため、多くのプレッシャーが軽減されます。

その事実、および密結合が少ない、再利用性が高いなどのその他の利点も、「定数」を分割するのに時間を費やすことは結局それほど悪い考えではなかったことも示した;-)

Update2

この質問はまだいくつかの注目を集めているので:
ここ数年、私がここまでやってきたことは次のとおりです。

すべての定数、変数などを、関連するスコープに正確に配置します。単一のメソッドでのみ定数を使用する場合は、そのメソッドで定数を定義しても問題ありません。単一のクラスに関心がある場合は、そのクラスのプライベート実装の詳細として残してください。名前空間、モジュール、プロジェクト、会社の範囲についても同様です。ヘルパー関数などにも同じパターンを使用しています。 (公開フレームワークを開発する場合、これは100%に当てはまらない場合があります)

これを行うと、再利用性、テスト性、保守性が向上し、コンパイル時間(少なくともC++)が短縮されるだけでなく、バ​​グ修正にかかる時間も短縮されるため、実際に新しい機能を開発する時間を増やすことができます。同時に、より多くのコードをより簡単に再利用できるため、これらの機能の開発は速くなります。これは、中央の定数ファイルが持つ利点をはるかに上回ります。

詳細については、特に インターフェース分離の原則単一の責任の原則 をご覧ください。

あなたが同意するなら、このアップデートは基本的に彼が言ったことに対するより一般的な見方なので、カレブの答えに賛成票を投じてください。

35
Tim Meyer

クラスに固有の定数は、そのクラスのインターフェースに入れる必要があります。

実際に構成オプションである定数は、構成クラスの一部である必要があります。そのクラスの構成オプションにアクセサーを提供する場合(および定数の代わりにそれらを使用する場合)、いくつかのオプションを変更するときに全世界を再コンパイルする必要はありません。

クラス間で共有されているが、構成可能ではない定数は、適切にスコープを設定する必要があります。特定の用途を持つファイルにそれらを分割して、個々のクラスが実際に必要なものだけを含めるようにしてください。これにより、これらの定数の一部を変更した場合のコンパイル時間が短縮されます。

33
Caleb

単純に言うと、巨大な定数クラスを、たとえばフォームごとに1つずつ、多数の小さなファイルに分割したいとします。これにより、定数ファイルにそれほど大きな依存関係がなくなるため、文字列を追加または更新しても、全体を再コンパイルする必要はありません。これらのファイルを中央の場所に保存することもできますが、(たとえば)ダイアログごとに定数が適切に付けられた1つのファイルがあります。次に、関連するダイアログファイルにそれらのファイルのみを含めることができます。これにより、再コンパイルが大幅に削減されます。

GNU GetTextユーティリティを使用して文字列を処理することもお勧めします。これは翻訳用に設計されていますが、単にテキストを別のものに変更するのと同じように機能します。文字列リソースですが、IDでキーが設定されているため、操作が難しいことがわかりました。GetTextユーティリティは元の文字列でキーが設定されているため、開発が非常に簡単です。

6
gbjbaanb

注:私はC++開発者ではありません...しかし、これが私の考えです。構成ファイルの使用の違いについての@jkのコメントに従うことを検討する必要があります。 DotNetには、そのような情報を格納するために使用されるリソースファイルがあります。 Windowsフォームでは、リソースファイルは各フォームのVSから維持されます。

共有する必要があるグローバル定数でない限り、定数の値がその使用範囲外に配置されることはありません。あなたが言ったように、これは少なくとも開発中に維持するのは難しいでしょう。また、名前の衝突が発生する可能性があります。もう1つは、特定の定数を誰が使用しているかを知るのが難しい場合があることです。

プログラマー以外のユーザーに情報を確認してもらいたい場合は、GUIの場合、画面をキャプチャします。データテーブルのエントリを確認してもらいたい場合は、データをExcelなどにエクスポートできます。

集中化されたロケーションアプローチを引き続き使用し、すべての定数を1つの大きなファイルに配置する場合は、各開発者が各インターバルの最後に中央ファイルに更新される共有ファイルを使用できます。データは、開発で使用される個々のファイルから取得されます。これは簡単に自動化することも手動で行うこともできます。ただし、先に述べたように、それはおそらくあなたが取る必要のないリスクです。

2
NoChance

一般的な解決策はありません。定数のパフォーマンス、使いやすさ、セキュリティ、ライフサイクルについて自問してください。

それらがそれらのスコープに近く定義されるほど、パフォーマンスは高くなります。

それらが論理的にグループ化され、その範囲外にあるほど、再利用性は高くなります。

コスタントがアクセスしにくいほど、セキュリティは高くなります。

定数の有効期間が長くなるほど、定数をどこに置いて使用できるかを気にする必要がなくなります。

バージョン番号のような定数は、ある種のマニフェストで定義されます。エラー関数のエラーコードはクラス内で定義されます。エラーコードは、有効期間が長いものである可能性があります(=ほとんど変更されません)。それを定数ファイルに入れることは、不要なものでファイルをスパムするだけです。

定数が定数の性質を持っているが、変数(バージョン番号など)が少ないほど、外部に置くことができます。定数の変数が少ないほど、定数が大きいほど、スコープ内に配置する必要があります。デバッグ中は、コンパイル時間を短縮するために外部に配置するのが理にかなっています。

ただし、最初の問題はコンパイル時間です。だから問題はあなたが正しい質問をするかどうかです。アプリケーションのコンパイル時間が長すぎる場合は、それをよりモジュール化して、パーツが互いに独立して機能するようにする方法を考えるべきです。部分的にコンパイルして、独立してテストします。単体テストが適切に行われ、本格的な場合(これは実際には多くの作業です)、それを心配することなく、かなり簡単にシフトできます。そして、問題はまったく別のドライブを取得します。

2
Christian

これらすべての定数をなんらかの構成ファイルに入れることをお勧めします。 Javaアプリケーションの場合、通常は.propertiesファイルを使用します。これは、各行が「(key)=(value)」としてフォーマットされた単純なテキストです。)例

 MainPanel.Title =アプリケーションへようこそ
 DB.table.users = TBL_USERS 
 logging.filename = application.log 

次に、実行時にこのファイルをロードし、キーを検索して値を取得できるようにするキャッシュを設定します。定数が必要な場合は、キャッシュをクエリします。キーはどこかに置く必要があり、キャッシュはグローバルにアクセスできる必要がありますが、定数の実際のvaluesを変更すると、再コンパイルが必要な場合は、アプリを再起動するだけで十分です(または、本当に凝りたい場合は、複数の.propertiesファイルとキャッシュを用意して、アプリケーションに実行時にキャッシュを再読み込みできるようにします)。

実装について、私はこれを見つけましたSO質問: https://stackoverflow.com/questions/874052/properties-file-library-for-c-or-c =(これはGoogle検索の最初のヒットでした-私は実際にこのソフトウェアを自分で使用していません)。