定数をどこに置くべきか、そしてその理由は?


34

私たちの大部分が大規模なアプリケーションでは、通常、「定数」の場所はわずかしかありません。

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

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

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

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

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

ただし、いくつかの欠点があります。

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

そのような定数をどこに保存しますか?また、プロジェクトマネージャーに、上記の利点に適合するより優れた概念があることを納得させるために、どのような議論を提起しますか?

C ++固有の回答または独立した回答をお気軽にお寄せください。

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

このプロジェクトの更新

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

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

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

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

Update2

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

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

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

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

あなたが同意する場合、この更新は基本的に彼が言ったことに対するより一般的な見解なので、カレブの答えに賛成です。


2
個人的には、定数にUIタイトルやメッセージ文字列はまったくありません。それらをapp.configに保存します
jk)。

1
私はあなたの現在のやり方が好きです-あなたの不利な点を理解していますが、私たちはそれを処理しなければならないかもしれません。
bigtang

1
大規模なJavaプロジェクトでもまったく同じ問題が見られました。1つの巨大な「定数」インターフェースで、その中の何かを変更し、Eclipseが再コンパイルされるまで15分待ちます。私はCalebと一緒にいます。定数を使用するコードの近くで、それらが自然に属する定数をグループ化します。定数であるため、それらを分離しておくことは、役に立たないOCDの練習です。
マイケルボルグワード

密結合は問題ではありません。それが実際に必要なことだからです。あなたはしたいあなたの定数ファイル内の1つの変更はおそらくソースファイルの多くに影響を与えます。(もちろん、他の問題もあります)。
gnasher729

@ gnasher729これは、多くのクラスが同じ定数を使用する場合にのみ当てはまります。クラスを、関係のない定数に密結合させたくないでしょう。コピーしたり、独立したテストを実行したりせずに、別のプロジェクトで再利用を試みるまで、最初は問題に思えないかもしれません
Tim Meyer

回答:


29

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

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

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


1
興味がある場合:私はあなたの答えや他の人に従って、私が達成したことで質問を更新しました
ティムマイヤー

6

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

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


それを試してみるかもしれません。タイトルなどの定数クラスはいくつかの構造体に分割されているため、構造体ごとに1つのクラスを開始することができます。GNUのことについては定かではありませんが、通常は、実行時、開発時のみに文字列を変更したくありません。ただし、将来別の言語に翻訳する必要がある場合に備えて、Qtの翻訳メカニズムを使用しています。
ティムマイヤー

興味がある場合:私はあなたの答えと他の人に従って、私が達成したことで質問を更新しました
ティムマイヤー

2

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

共有する必要のあるグローバル定数でない限り、使用範囲外に配置される定数の値は表示されません。あなたが言及したように、これは少なくとも開発中に維持することは困難です。また、名前の衝突が発生する場合があります。別のことは、だれが与えられた定数を使用しているかを知るのが難しいかもしれないということです。

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

それでも中央集中型の方法を使用し、すべての定数を1つの大きなファイルに配置する場合、各開発者は各間隔の終わりに中央ファイルに更新される共有ファイルを使用できます。データは、開発で使用される個々のファイルから取得されます。これは簡単に自動化するか、手動で実行できます。しかし、私が言ったように、おそらくあなたが取る必要がないリスクです。


2

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

スコープに近いほど、パフォーマンスが高くなります。

論理的にグループ化され、スコープ外にあるほど、再利用性が高くなります。

共助者がアクセスしにくいほど、セキュリティは高くなります。

定数の有効期間が長くなるほど、定数をどこに配置するかを気にしなくなります。

バージョン番号のような定数は、何らかのマニフェストで定義されます。エラー関数のエラーコードは、クラス内で定義されます。エラーコードはおそらく寿命が長いものです(=ほとんど変わらない)。定数ファイルに入れると、不要なものがファイルにスパム送信されます。

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

ただし、最初の問題はコンパイル時間です。したがって、問題はあなたが正しい質問をするかどうかです。アプリケーションのコンパイル時間が長すぎる場合は、各モジュールが独立して動作するようにモジュール化する方法を検討する必要があります。部分的にコンパイルし、独自にテストします。単体テストが適切に行われ、本格的である場合(実際には多くの作業です)、そのことを心配することなく、かなり簡単に内容を変更できます。そして、質問は全く異なるドライブを取得します。


1

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

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

次に、このファイルを実行時にロードし、キーを検索して値を取得できるキャッシュを作成します。定数が必要な場合は、キャッシュを照会します。あなたはまだどこかにキーを持っている必要があり、キャッシュはグローバルにアクセス可能である必要がありますが、定数の実際のを変更する場合、再コンパイルは必要ありません、アプリを再起動するだけで可能です(またはあなたは本当に空想を得て、複数の.propertiesファイルとキャッシュを持ち、アプリケーションが実行時にキャッシュをリロードできるようにしたいです)。

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


C ++プロジェクトの場合、値を変更する場合にのみ定数ファイルを再コンパイルする必要があります。これは、値が.cppファイルで割り当てられるためです。ただし、定数の追加/削除/名前の変更/移動には、完全な再構築が必要です
Tim Meyer

@TimMeyer:定数を複数のファイルに分割する場合、定数の追加/削除はその特定のファイルに依存するファイルにのみ影響しますか?
FrustratedWithFormsDesigner

正しい。主な問題は、私はそれを示唆している場合、人々は私が「利点」として記載されているものの一つに言及する傾向があるということです迷子
ティム・メイヤー

ただし、実際にはいくつかの定数ファイルを同じ場所に置くことは考えていませんでした
Tim Meyer

+1。@Tim Meyer:この目的のために、コンパイル時のチェックは、節約するよりもはるかにコストがかかります。また、国際化が必要な場合はどうしますか?
ケビンクライン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.