web-dev-qa-db-ja.com

単一文字定数はリテラルよりも優れていますか?

私は最近、ほぼすべての単一文字を定数として提供するクラスに遭遇しました。 COMMAからBRACKET_OPENまで。これが必要かどうか疑問に思っています。私は "article" を読みました。これは、1文字のリテラルを定数にプルすると役立つことを示唆しています。だから、私は懐疑的です。

定数を使用する主な魅力は、変更が必要になったときにメンテナンスを最小限に抑えることです。しかし、コンマを表すために「、」以外の記号をいつ使用するのでしょうか。

リテラルの代わりに定数を使用する理由は、コードを読みやすくするためだけです。しかし、city + CharacterClass.COMMA + state(たとえば)はcity + ',' + stateよりも本当に読みやすいですか?

私にとって、短所は長所を上回ります。主に、別のクラスと別のインポートを導入することです。そして、私は可能な限り少ないコードを信じています。だから、私は一般的なコンセンサスがここにあるのだろうかと思っています。

128
Austin Day

Tautology

質問の最初の文を読んだ場合、この質問はのような適切な使用についてではないことは明らかです マジックナンバー 、それはせいぜいひどい愚かな愚かさ一貫性についてです。これがこの回答の対象です

常識では、_const char UPPER_CASE_A = 'A';_または_const char A = 'A'_を使用しても、システムのメンテナンスと複雑さ以外は何も追加されません。 _const char STATUS_CODE.ARRIVED = 'A'_は別のケースです。

Constantsは、実行時に不変なものを表すことになっていますが、コンパイル時に将来変更する必要があるかもしれません。 _const char A =_がA以外のものと正しく一致するのはいつですか?

Javaコードに_public static final char COLON = ':'_が表示されている場合は、それを書いた人を見つけてキーボードを壊してください。COLONの表記が_:_から変更されている場合は、メンテナンスの悪夢があります。

難読化:

誰かがそれを_COLON = '-'_に変更するとどうなるでしょうか。なぜなら、使用している場所ではどこでも_-_が必要だからです。 const参照ごとに基本的にassertThat(':' == COLON)と言うユニットテストを記述して、変更されないことを確認しますか?彼らがそれらを変更するときに誰かにテストを修正させるだけですか?

誰かが実際に_public static final String EMPTY_STRING = "";_が有用で有益であると主張する場合、あなたは彼らの知識を修飾し、他のすべてについてそれらを安全に無視します。

namedバージョンですべての印刷可能な文字を使用できることは、誰がそれを行っても、監視されていないコードを書く資格がないことを示しています。

凝集:

また、人為的に凝集力を低下させます。なぜなら、それを使用し、関連しているものから遠ざけるからです。

コンピュータプログラミングでは、結束はモジュールの要素が一緒に属している程度を指します。したがって、凝集度は、特定のモジュール内の機能の断片間の関係の強さを測定します。たとえば、非常にまとまりのあるシステムでは、機能は強く関連しています。

カップリング:

また、関係のない多くのクラスを結合します。これらはすべて、実際には自分が行うこととは関係のないファイルを参照するためです。

密結合とは、クラスのグループが互いに大きく依存している場合です。このシナリオは、クラスが担当する責任が多すぎる場合、または1つの懸念が独自のクラスではなく多くのクラスに分散している場合に発生します。

_DELIMITER = ','_のようなbetter nameを使用した場合でも、名前は総称であり、意味を持たないため、同じ問題が発生します。値を再割り当てしても、インテリジェンス_','_を検索して置換する以外に、影響分析には役立ちません。一部のコードはそれを使用して_,_を必要とし、他の一部のコードは_;_を使用する必要があるのはなぜですか?それでも、すべての使用を手動で確認して変更する必要があります。

野生で:

私は最近、18歳の_1,000,000+ LOC_アプリケーションをリファクタリングしました。 _public static final COMMA = SPACE + "," + SPACE;_のようなものがありました。これは、必要な場所に_" , "_をインライン化することよりも優れています。

読みやすさを主張したい場合は、IDEを表示するようにwhitespace文字を表示するように設定する方法を学ぶ必要がありますそれが何であれ、それはシステムにエントロピーを導入する非常に怠惰な理由です。

また、_,_が複数回定義されており、複数のパッケージおよびクラスでWord COMMAの複数のスペルミスがありました。コード内で混合されたすべてのバリエーションへの参照。完全に無関係なものを壊すことなく、何かを試してfixするのは悪夢に他なりません。

アルファベットと同様、複数の_UPPER_CASE_A_、A、_UPPER_A_、_A_UPPER_があり、ほとんどの場合Aしかし、場合によってはそうではなかった。ほとんどすべての文字についてですが、すべての文字についてではありません。

そして、編集履歴から、これらの1つが18年間に編集または変更されたようには見えませんでした。現在明らかな理由は、追跡できない多くのものが壊れてしまうためです。このため、新しい変数があります。同じ理由で変更できない決して同じものを指す名前。

正気でない現実では、この実践は何もしておらず、最大のエントロピーから始めることを主張することはできません。

私はこれらすべての混乱をリファクタリングし、すべてのトートロジーをインライン化しました。新しいカレッジの採用は、これらのconstの参照が実際に示した複数のレベルの間接参照を探す必要がなかったため、はるかに生産的でした。彼らが何と名付けられたのか、何を含んでいたのかについて信頼できる。

182
user7519

定数を使用することの主な魅力は、変更が必要な場合のメンテナンスを最小限に抑えることです。

絶対違う。これはまったくない定数は定義によって変更されないであるため、定数を使用する理由です。定数が変更された場合それは定数ではありませんでしたか?

定数を使用することの魅力は、変更管理とは何の関係もありませんプログラムを人々が記述、理解、維持できるようにする。コロンがURLセパレーターとして使用されているプログラムのすべての場所を知りたい場合、一定のURLSeparatorを定義するための規律があれば非常に簡単にそれを知ることができます。 :および:が基本クラス、または?:演算子などを示すために使用されているコード内のすべての場所を取得します。

これは無意味な時間の無駄であると述べている他の回答には完全に同意しません。名前付き定数はプログラムに意味を追加します。これらのセマンティクスは、人間とマシンの両方がプログラムをより深く理解し、より効果的に維持するために使用できます。

ここでの秘訣は、定数を避けることではなく、構文プロパティではなくセマンティックプロパティで名前を付けることです。使用されている定数は何ですか?プログラムのビジネスドメインがタイポグラフィや英語解析などでない限り、それをCommaと呼ばないでください。 ListSeparatorまたはそのようなものを呼び出して、その意味を明確にします。

145
Eric Lippert

いいえ、それはばかげています。

ローカリゼーションの理由から、notとは何ですか?たとえば、千単位の区切り文字isアメリカではカンマ(1,000,000)ですが、他のロケールではnotコンマです。それを名前付きラベル(適切な非コンマ名を使用)にプルすると、プログラマーはそれらの詳細を無視または抽出できます。

しかし、「魔法のひもが悪い」ので、定数を作ることは、単に貨物を養うことです。

61
Telastyn

あいまいになる可能性がある文字や、いくつかの異なる目的で使用される文字がいくつかあります。たとえば、'-'ハイフン、マイナス記号、さらにはダッシュとして。次のように別々の名前を付けることができます。

static const wchar_t HYPHEN = '-';
static const wchar_t MINUS = '-';
static const wchar_t EM_DASH = '-';

後で、コードを次のように再定義して、曖昧さをなくすようにコードを変更することを選択できます。

static const wchar_t HYPHEN = '-';
static const wchar_t MINUS = '\u2122';
static const wchar_t EM_DASH = '\u2014';

それがconsider定数をcertain単一文字に定義する理由かもしれません。 ただし、このようにあいまいな文字の数は少ない。せいぜい、あなたはそれらのためだけにそれをするでしょう。この方法でコードを因数分解する前に、あいまいな文字を実際に区別する必要があるまで待つこともできると私は主張します。

表記上の規則は言語や地域によって異なる可能性があるため、このようなあいまいな句読点を変換テーブルからロードする方がよいでしょう。

29
Adrian McCarthy

定数は意味を追加する必要があります。

COMMAをコンマとして定義しても、コンマはコンマであることがわかっているため、意味がありません。代わりに、意味を破棄します。COMMAは実際にはもうコンマではない可能性があるためです。

目的にコンマを使用し、名前付き定数を使用したい場合は、目的に合わせて名前を付けます。例:

  • _city + CharacterClass.COMMA + state_ =悪い
  • _city + CITY_STATE_DELIMITER + state_ =良い

フォーマットに関数を使用

私は個人的にFormatCityState(city, state)を好み、関数の本体が短く、テストケースに合格する限り、その本体がどのように見えるかを気にしません。

22
Peter

定数COMMAが','または","よりも優れているという考えは、簡単にバンキング解除することができます。たとえば、final String QUOTE = "\"";を使用すると、すべてのスラッシュなしで読みやすさが大幅に節約されますが、\'" Iなどの言語制御文字は禁止されます。それらは非常に有用であることがわかりませんでした。

final String COMMA = ","の使用は、形式が悪いだけでなく、危険です。セパレーターを","から";"に変更したい場合、定数ファイルをCOMMA = ";"に変更する方が高速です。ただし、COMMAを使用していた他のすべてのものも、外部のコンシューマに送信されるものも含めて、セミコロンになっています。したがって、すべてのテストに合格します(マーシャリングおよび非マーシャリングコードもすべてCOMMAを使用していたため)が、外部テストは失敗します。

便利なのは、それらに便利な名前を付けることです。そしてはい、複数の定数が同じ内容で異なる名前を持つことがあります。たとえば、final String LIST_SEPARATOR = ","です。

だからあなたの質問は「リテラルよりも単一の文字定数が優れている」であり、答えは一義的にいいえ、そうではありません。しかし、それらの両方よりもさらに優れているのは、その目的が何であるかを明示的に示す狭いスコープの変数名です。確かに、これらの追加の参照には数バイト余分に費やします(それらはあなたにコンパイルされないと仮定しますが、おそらくそうなるでしょう)長期のメンテナンスでは、アプリケーションのコストのほとんどは、作る時間の価値があります。

17
corsiKa

レクサーとパーサーを作成し、整数定数を使用して端末を表す作業をいくつか行いました。 1文字の端末では、たまたまASCIIコードが数値として単純化のためにコードとして使用されていましたが、コードは完全に別のものである可能性があります。したがって、ASCIIが割り当てられたT_COMMAが必要です。 -、 '、'の定数値としてのコード。ただし、ASCIIセット以上の整数が割り当てられた非終端記号の定数もありました。yaccやbisonなどのパーサージェネレーター、またはパーサーを見るとこれらのツールを使用して書かれた私は、基本的に誰もがそれをした方法であるという印象を得ました。

だから、他の人と同じように、コード全体でリテラルの代わりに定数を使用するという明確な目的のために定数を定義することは無意味だと思いますが、コードでだまされたコードに遭遇する可能性があるEdgeケース(パーサーなど)があると思いますあなたが説明するような定数。パーサーの場合、定数は文字リテラルを表すためだけのものではないことに注意してください。それらはhappenが文字リテラルであるエンティティを表します。

対応するリテラルの代わりに定数を使用することが理にかなっているいくつかの孤立したケースを考えることができます。たとえば、UNIXのボックスではNEWLINEをリテラル '\ n'と定義しますが、WindowsまたはMacのボックスでは '\ r\n'または '\ n\r'とします。表形式のデータを表すファイルを解析する場合も同様です。 FIELDSEPARATORおよびRECORDSEPARATOR定数を定義できます。これらの場合、実際には、特定の機能を果たす文字を表す定数を定義しています。それでも、初心者のプログラマーであれば、フィールドセパレーター定数にCOMMAという名前を付け、FIELDSEPARATORと呼ばれるべきだったことに気付かなかったかもしれません。気づいたときには、コードは本番環境にあり、次の段階にいます。プロジェクトなので、誤って名前が付けられた定数はコードに残り、誰かが後で見つけて首を振ります。

最後に、特定の文字エンコーディング(iso-8859-1など)でエンコードされたデータを処理するコードを記述する場合にmightを記述する方法は理にかなっていますが、後でエンコーディングが変更されることを期待します。もちろん、そのような場合、ローカリゼーションまたはエンコードおよびデコードライブラリを使用してそれを処理する方がはるかに理にかなっていますが、何らかの理由でそのようなライブラリを使用してエンコードの問題を処理できなかった場合は、定数のみを使用します。ソースコード全体に散らばっているハードコードされたリテラルの代わりに、単一のファイルで再定義する必要があります。

あなたがリンクした記事に関して:私はそれが文字リテラルを定数で置き換えることを主張することを試みているとは思わない。インターフェイスを使用して定数をコードベースの他の部分に取り込む方法を説明しようとしていると思います。これを説明するために使用されるサンプルの定数は非常に不適切に選択されていますが、私はそれらが何らかの方法で重要であるとは思いません。

3
Pascal

ここでのすべての良い答えに加えて、私は思考のための食べ物として追加したいと思います、その良いプログラミングは適切なabstractionsを提供することですこれは、同じコードを何度も繰り返す必要なく、自分や他の人が構築できます。

優れた抽象化により、一方ではコードが使いやすくなり、他方では保守が容易になります。

私は完全にDELIMITER=':'自体は貧弱な抽象概念であり、COLON=':'よりも優れているだけです(後者は完全に貧弱であるため)。

文字列とセパレータを含む優れた抽象化には、1つ以上の個別のコンテンツアイテムを文字列にパックし、何よりもまず、区切り文字を伝える前に、パックされた文字列からそれらをアンパックする方法が含まれます。このような抽象化は、ほとんどの言語でクラスとして概念としてバンドルされます。たとえば、このクラスが使用されるすべての場所を検索し、抽象化が使用される各ケースでパックされた文字列の形式に関するプログラマの意図を確信できるという点で、その使用は実質的に自己文書化されます。

このような抽象化が提供されると、DELIMITERまたはCOLONの値を確認する必要がなく、簡単に使用できます。また、実装の詳細の変更は通常、実装。つまり、これらの定数は、適切な抽象化の中に隠された実装の詳細である必要があります。

定数を使用することの主な魅力は、変更が必要な場合のメンテナンスを最小限に抑えることです。

通常、いくつかの関連する機能を組み合わせた優れた抽象化は、メンテナンスを最小限に抑えるのに適しています。まず、プロバイダーとコンシューマーを明確に区別します。次に、実装の詳細を隠し、代わりに直接役立つ機能を提供します。第三に、彼らはいつどこで使用されているかを高いレベルで文書化します。

3
Erik Eidt

このような定数が効果的に使用されているのを目にしたのは、既存のAPIまたはドキュメントと一致させることです。 COMMAのようなシンボルが使用されているのを見てきました。特定のソフトウェアが、抽象構文ツリーのタグとしてCOMMAを使用するパーサーに直接接続されているためです。また、正式な仕様と一致するために使用されることも確認しました。正式な仕様では、','ではなくCOMMAのような記号が表示されることがあります。これは、可能な限り明確にする必要があるためです。

どちらの場合も、COMMAのような名前付きシンボルを使用することで、他の方法ではばらばらの製品にまとまりを与えることができます。その値は、過度に冗長な表記のコストを上回ることがよくあります。

2
Cort Ammon

リストを作成しようとしていることを確認してください。

したがって、次のようにリファクタリングします:String makeList(String[] items)

つまり、dataの代わりにlogicを除外します。
言語はリストの表現方法が異なる場合がありますが、コンマは常にコンマです(これはトートロジーです)。したがって、言語が変更された場合、コンマ文字を変更しても役に立ちませんが、これは役立ちます。

2
user541686

これが他の開発者によってアプリケーションの一部として記述されたクラスである場合、これはほぼ間違いなく悪い考えです。他の人がすでに指摘したように、値を変更できるSEPARATOR = ','のような定数を定義することは理にかなっています。

ただし、名前がその内容を正確に表す定数を宣言することが理にかなっている場合と、定数の名前を適切に変更しないと値を変更できない場合には、少なくとも2つのケースがあります。

  • 数学または物理定数、例えばPI = 3.14159。ここで、定数の役割は、記号名PIが表す値よりもはるかに短くて読みやすいため、ニーモニックとして機能することです。
  • パーサー内の記号またはキーボードのキーの完全なリスト。 ほとんどまたはすべてのUnicode文字を含む定数のリストを作成することは理にかなっているかもしれませんそしてこれはあなたのケースが当てはまるかもしれないところです。 Aなどの一部の文字は、明確ではっきりと認識できます。しかし、АAを簡単に区別できますか?最初の文字はキリル文字Аで、後者はラテン文字A 。グラフィカルにほとんど同じであるにもかかわらず、それらは異なる文字であり、異なるUnicodeコードポイントで表されます。ほとんど同じに見える2つの文字ではなく、コードに定数CYRILLIC_CAPITAL_ALATIN_CAPITAL_Aを使用したいと思います。もちろん、これはキリル文字を含まないASCII文字のみで作業することを知っている場合は無意味です。同様に、私は日常的にラテンアルファベットを使用しているので、プログラムを作成していた場合漢字が必要な場合は、理解できない文字を挿入するのではなく、定数を使用することをお勧めします。漢字を日常的に使用している人にとっては、漢字は明白かもしれませんが、ラテン文字の方が簡単かもしれません名前付き定数として表現するためです。つまり、ご覧のとおり、コンテキストに依存します。ただし、作成者はライブラリの使用方法と文字を事前に知ることができないため、ライブラリにはすべての文字の記号定数が含まれている場合があります特定のアプリケーションで読みやすくするために定数が必要になる場合があります。

ただし、このようなケースは通常、システムクラスまたは特別な目的のライブラリによって処理され、アプリケーション開発者が作成したコードでの発生は、非常に特別なプロジェクトで作業している場合を除いて非常にまれです。

0