特定の値を文字列として保存するのは悪い習慣ですか?


19

それは非常に曖昧なタイトルですが、私はそれを表現するより良い方法を考えることができませんでした。しかし、ちょうど一例として、ゲーム内のキャラクターが動いている方向について考えてください。文字列を使用してからのようなことをするのはちょっと間違っているように感じif(character.direction == "left")ます。の代わりにを誤って使用しLeftたりl、他の何かを使用したりするなど、愚かなエラーの余地が多すぎるように思えleftます。私の疑いは正しいですか?もしそうなら、このような何かを達成するための好ましい方法は何ですか?


10
あなたの言語がそれらをサポートしている場合、列挙型のほかに?
hoffmale

12
Stringly Typedを意味しますか?
ラバーダック16


一部の言語では、文字列として以外の値を保存できませんbash
mouviciel

理由はわかりませんが、Cの「定義」を思い出しました。次のようなことができます。#define false true
linuxunil

回答:


28

使用している言語が列挙型の使用をサポートしている場合、それらを使用します。特定のタイプで使用可能なオプションの数を制限できます。例:Javaの場合:

public enum Direction {
    LEFT,
    RIGHT,
    UP,
    DOWN
}

2
この回答は、OPの問題を理解するのに役立ちません。ここには理由はありませんが、enumjavaのような上品な言語の範囲に限定された意見のみです。多くの言語(VBAなど)には enumがありますが、同じ将来性を備えていないため、最良の選択肢ではないかもしれません。なぜenum単独では不完全で、多くの場合脆弱で、言語固有のソリューションであるかの議論については、私の答えをご覧ください。
sqykly 16

12

コードでリテラル文字列(またはマジックナンバー)を使用するのはひどい習慣です。

列挙型は優れているか、少なくとも定数を使用します(もちろん、列挙型は1つ以上の関連する定数の単なるラッパー型です)。

const string LEFT = "left"
if (someThing == LEFT) { doStuff(); }

この単純なスイッチには多くの利点があり、それらの説明をどこから始めればよいかさえわかりません(少なくともコーヒーがなければ)。マジックナンバーを読んでください。これはまったく同じ概念で、文字列値ではなく数値にのみ適用されます。

ここに簡単な参考文献があります、もっとたくさんあると確信しています:https : //stackoverflow.com/questions/47882/what-is-a-magic-number-and-why-is-it-bad


1
このアプローチに伴う問題は、--const string LEFT = "letf"-のようなタイプミスが発生することです。一方、コンパイル時にキャッチされる列挙型を使用する場合です。
ピーターB

@PieterB-OPの質問は十分に広く、「左」の例は単なる任意の例で、すべてのケースが列挙型に一致しないと考えました。具体的には、一連の方向については列挙型が最良の選択であることに同意しますが、私の答えは、「リテラルに入れた場合、少なくとも定数にする」という点でより一般的であることを意味しました(もちろん、列挙、 )一定のタイプである
jleach

3
名前が「s」で始まる曜日を週末の曜日に割り当てたアプリケーションで作業することができました。フランスが1日の週末しかなかったアプリケーションを国際化したとき、彼らが「事実」について非常に驚いた。
ピーターB

4
これをマイクロ最適化と呼びますが、一部の言語ではこれを値の比較として解釈し、文字列の各文字をチェックするのに多くの時間を浪費する場合があります。質問者が行ったように、ハードコーディングされた文字列「左」と比較すると、さらに可能性が高くなります。定数値を文字列にする必要がない場合(つまり、印刷しない場合)、代わりにconst intを使用することをお勧めします。
デブスマン

4

簡単な答え:はい、文字列は一連のテキスト文字の保存とアクセス以外のタスクには理想的ではありません。抽象化の基礎となるビットが文字列であっても、変数または定数として参照することには利点があります

長い答え:ほとんどの言語は、問題のドメインに近い型を提供します。そうでない場合でも、おそらく(etcなど)leftとは異なるエンティティとして定義できる方法がありますright。を使用して文字列に変換すること"left"は、単に言語およびエラーチェックなどのIDEの便利な機能を失うことです。使用する必要がある場合でも

left = "left";

または何か同等のものを使用すると、コード全体で文字列を参照するときに文字列を使用する利点があります。例えば、

if (player.direction == Left)

コンパイル時エラー(ベストケース、javaなど)として検出されるか、またはそのビットに相当するIDEによってフラグが付けられます(最悪の場合、基本など)。

さらにleft、参照可能なエンティティとしての概念を持つことは、方向のタイプに応じて決定する方向に対応するのに役立ちます。を使用することにより"left"、方向はになりStringます。型のより良い抽象化を見つけたり作成したりする場合は、コード本体全体を探索して、のすべてのインスタンスを変更する必要があります"left"。型が作成されている場合、定数または変数を変更する必要はありません。

本当に必要なタイプは、ドメインに固有のものです。あなたのコードの計画は何leftですか?おそらく、左を向いている、または前方に移動しているテクスチャまたはスプライトのx軸をミラーリングしたいでしょう。その場合、leftその動作を反映するプロパティまたはメソッドを使用してrightオブジェクトを作成し、オブジェクトは反対のミラー化されていない結果になる場合があります。スプライトまたはテクスチャを表すオブジェクトでそのロジックを実行できます。この場合も、上記の理由では"left"なく使用することに苦しむでしょうleft

Java(およびおそらく私がまだ知らない他の言語)ではenum、Java の場合はインスタンスの有限セットとenum同様に有用であるため、優れた代替手段ですfinal class。必要に応じてビヘイビアを定義できます。またenum、を宣言するファイルの外部で手間をかけずにオープンクラスに切り替えることができますが、多くの言語はをenumプリミティブタイプと見なすか、使用するために特別な構文を必要とします。それenumは、それと関係のないコードにフードをリークし、後で修正する必要があるかもしれません。enumオプションを検討する前に、言語の概念を理解してください。


2

言語を指定しませんでしたが、一般的な答えを出すには、何かを表すのではなく、実際にテキストが必要なときに文字列を使用する必要があります。たとえば、あなたが与えた例は、実稼働ソフトウェアでは受け入れられません。

すべての言語にはさまざまな基本データ型があり、タスクに最も適したデータ型を使用する必要があります。この例を使用するには、数値定数を使用してさまざまな状態を表すことができます。したがって、leftの値は0で、rightの値は1です。あなたが言及した理由に加えて、数値はより少ないスペース(メモリ)を占有し、複数の文字列を比較するよりも数値を比較する方が常に高速です。

提案されているように、言語で許可されている場合は列挙を使用します。あなたの特定の例では、明らかにそれがより良い選択です。


速度とメモリは重要ではありません。つまり、列挙型または定数を優先します。データがXMLなどのテキスト形式から来ている場合、文字列はより意味がありますが、それでもすべての大文字またはすべての大文字と一致します。または、常に変換します、例えば。if (uppercase(foo) == "LEFT")
user949300

2
@ user949300文字列をすべて大文字またはすべて小文字に変換することも信頼できません。誰かがトルコのような別のロケールを訪問する(またはそこでビジネスを拡大する)ことを決定し、単純な大文字(または小文字)の文字列比較を破る可能性があります。
8ビットツリー

速度とメモリを気にすることは常に良い習慣であり、無限のリソースではありません。読みやすさや他のトレードオフのために意識的に犠牲にしても構いませんが、ここではそうではありません。しかし、それらを気にしないと、アプリケーションの動作が遅くなったり、無駄になりがちです。
Nagev 16

文字列の比較は、enumの比較よりも50倍遅い可能性があります。ほとんどの場合、それは重要ではありません。ただし、コードがすでに低速側にある場合は、その違いにより受け入れられない可能性があります。もちろん、より重要なのは、文字列を使用するとコンパイラがスペルミスを助けられないという事実です。
gnasher729

1

状況に合ったデータ型を使用してください。

string基になる内部/アプリケーション内通信としての型の使用は、本質的に問題ではありません。実際には、時にはそれがだ望ましい文字列を使用する:あなたは、パブリックAPIを使用している場合、それは人間が読めるようにAPIの中に出入り値のために望ましい可能性があります。APIを使用している人が学習やデバッグを簡単に行えるようになります。

コードの本当の問題は、値を繰り返すことにあります。

これをしないでください:

if (direction == 'left') {
  goLeft();
}

これらの条件のいずれかまたは「左」のその他の使用方法のいずれかでタイプミスをした場合、タイプミスしたため、コードベース全体の検索を効果的に実行できない可能性があります。

代わりに、使用している言語で必要なデータ型をサポートしているものに応じて、列挙または定数に対するコードを作成します。

手段はそれLEFTに、割り当てる必要があります一度一度だけ、アプリケーションインチ そして、残りのコードはこれらの列挙型または定数を活用する必要があります。

const string LEFT = "left";

if (direction == LEFT) {
  goLeft();
}

これにより、必要に応じて、後でルートに使用するデータ型をより簡単に変更することもできます。


1

それは悪い習慣ですか?

おそらくはい、それは依存正確にあなたが何をしているか、また、使用しているプログラミング言語。

あなたの例では、方向が取ることができる値のセットが小さく固定されていることが推測できます。この場合、文字列を使用する利点はなく、明確な欠点もあります。この例では:

  • enumあなたのプログラミング言語がこれをサポートしている場合のタイプは、望ましいだろう。
  • それ以外の場合は、名前付き整数定数を使用して、定数を1つ定義します。

しかし、値のセットが動的に変更可能である場合、または時間とともに進化する必要がある場合、答えは異なる場合があります。ほとんどの言語では、enumタイプの変更(値の追加または削除など)を行うには、完全な再コンパイルが必要です。(たとえば、Javaで列挙値を削除すると、バイナリの互換性が失われます。)名前付き整数定数にも同じことが当てはまります。

このようなシナリオでは、別のソリューションが必要になる場合があり、文字列の使用はオプションの1つです。(そして、シナリオが動的拡張機能を備えた固定コアを必要とする場合は、文字列リテラルと名前付き定数を組み合わせることができます。)


Javaで実装している場合character.direction == "left"、別の理由で悪い習慣です。==文字列の同等ではなくオブジェクトのIDをテストしているため、文字列の比較には使用しないでください。("left"文字列のすべてのインスタンスがインターンされたことを保証できれば機能しますが、それにはアプリケーション全体の分析が必要です。)


1
固定されているように見えるものが、まったく固定されていないことがしばしば判明します。たとえば、4方向は8方向になります。
ジミーT.

@JimmyT。-それはコンテキストに依存します。たとえば、ゲームでは、キャラクターが移動できる方向の数を4から8に変更すると、ゲームルールとルールを実装するコードの完全なオーバーホールが必要になります。String方向を表すために使用すると、変更を加えるために必要な作業量にほぼゼロの差が生じます。より賢明なアプローチは、方向の数が変わらないと想定し、ゲームのルールが根本的に変わった場合、どちらの方法でも多くの作業が必要になることを認識することです。
スティーブンC

@JimmyT-私はこれが何度も見られたことに完全に同意します。また、開発者が文字列を再定義するショートカットを取得するので、たとえば、product = "Option"はproduct = "Option_or_Future"になり、コードの意味が完全に低下します。
クリスミルバーン

-3

提案されているように、Enumを使用する必要があります。ただし、Strignsの代わりにEnumを使用するだけでは不十分です。あなたの特定の例では、プレイヤーの速度は実際には物理学によるベクトルでなければなりません:

class Vector {
  final double x;
  final double y;
}

次に、このベクトルを使用して、ティックごとにプレーヤーの位置を更新する必要があります。

次に、それを列挙型と組み合わせて読みやすくします。

enum Direction {
  UP(new Vector(0, 1)),
  RIGHT(new Vector(1, 0)),
  DOWN(new Vector(0, -1)),
  LEFT(new Vector(-1, 0));

  private Vector direction;

  Direction(Vector v) {
    this.direction = v;
  }

  public Vector getVector() { return this.direction; }
}

4
-1あなたは彼が与える例に行き過ぎです。
ピーターB
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.