これはシンボリックな定数の過剰使用ですか?


34

私はソフトウェアエンジニアリングが初めてなので、学習演習としてチェスゲームを書きました。私の友人はそれを見て、私のコードが次のように見えることを指摘しました

for (int i = 0; i < 8; i++){
    for (int j = 0; j < 8; j++){

彼は代わりにあるべきだと主張した

for (int i = 0; i < CHESS_CONST; i++){
    for (int j = 0; j < CHESS_CONST; j++){

私が今考えるのが面倒ではないいくつかのより良いシンボル名で。

もちろん、私は一般的にマジックナンバーの使用を避けることを知っていますが、

  1. この数は変更されません。
  2. 番号はコード全体で非常に多くの場所で使用されているため、名前はとにかく説明的ではありません。そして
  3. チェスプログラムのソースコードを調べている人は、8の目的を知るためにチェスについて十分に知っている必要があります。

シンボリック定数は本当に必要ありません。

では、皆さんはどう思いますか?これはやり過ぎですか、それとも慣習に沿ってシンボルを使用する必要がありますか?


46
CHESS_CONSTは、数字の8を使用するよりもはるかに悪いですが、わかりやすい名前の定数を使用すると改善されます。あなたは誰もがコードの8が何を意味するかを知っているべきだと言いますが、これは真実ではありません。コンテキストのない整数リテラルは、動きの数、ボード上のピースの数など、いくつものことを意味します。定数の説明的な名前は、意図を明確にし、コードを理解しやすくします。
ジャックB

43
個人的には、マジックナンバーよりもずっと不快なのは、名前ijループ変数です。私の人生では、どちらがランクを表し、どちらがファイルを表すことになっているのかわかりません。ランクの範囲は1..8からファイルの範囲はa..hですが、あなたの場合、両方ij範囲は0..7なので、どちらがどちらなのかわかりません。私が知らない国際的なレター不足の危機はありますか、またはそれらの名前をrankandに変更することで何が問題になりfileますか?
ヨルグWミットタグ

4
悪魔の擁護者を一瞬演じます。マジックナンバー8を使用しているチェスコードを読んでいるところを想像してみてください。どうすれば100%確信できますか?他の何かを意味する可能性はありますか?あなたが仮定さえする必要がなかったなら、それは少し良くなったと思いませんか?仮定が正しいかどうかを判断するために、コードをトレースするのにどれくらいの時間を費やすでしょうか?代わりに意味のある洞察力のある名前を使用して、コードがより自己文書化されていた場合、時間を短縮しますか?
ベンコットレル

16
@JacquesB:確かに。8つのランク、8つのファイル、8つのポーンがあります。チェスエンジンの中には、特定のピース、特定のフィールド、特定の動きにポイントが関連付けられ、エンジンが最高スコアの動きを選択するポイントシステムを使用するものがあります。8はそのようなスコアかもしれません。デフォルトの検索深度は8です。それはほとんど何でもかもしれません。
ヨルグWミットタグ

9
例えばforeachループの使用を検討しますforeach(var rank in Ranks)。また、両方のループを、各要素が(ランク、ファイル)タプルである1つにマージすることを検討します。
-CodesInChaos

回答:


101

あなたの友人は、シンボリック名を使用するのが正しいですが、名前は間違いなくよりわかりやすい名前である必要があると思います(のBOARD_WIDTH代わりにCHESS_CONST)。

プログラムの存続期間中に数値が変更されない場合でも、プログラム内に番号8が別の意味で発生する他の場所がある場合があります。「8」をBOARD_WIDTHボード幅の意味で置き換え、別の記号名を使用すると、これらの異なる意味が明示的、明白になり、プログラム全体が読みやすく、保守しやすくなります。ボード幅に依存するコード内のすべての場所をすばやく特定する必要がある場合は、プログラム全体のグローバル検索(または環境で提供されている場合は逆シンボル検索)も実行できます。

番号の名前を選択する方法(または選択しない方法)については、この以前のSE.SEの投稿も参照してください。

サイドノートとして、コメントでここで説明されているため、実際のプログラムのコードで、変数がボードのi行と列を参照するjか、またはその逆の場合、変数名を選択することをお勧めしますrowおよびのように区別を明確にしcolます。そのような名前の利点は、間違ったコードを間違って見えるようにすることです。


22
OPが非常に優れたチェスエンジンを作成していて、3Dチェスまたはその他のバリアントを含めるように拡張したい場合は、8を変更する必要があるかどうかが課題になります。
スティーブバーンズ

32
@SteveBarnes:私はそのような引数を避けようとします、それは「このプログラムを異なる種類のゲームにすることは今までとはまったく異なると思うので、マジックナンバー8をそこに残すことができます」
Doc Brown

4
@IllusiveBrianこれは、2つの理由から古典的な「ストローマン」の引数です。(1)定数を定義するだけでは、プログラマが「8」(偶然、設計、悪意など)を入力できないことを意味しません。定数BOARD_WIDTH = 8があるからといって、プログラムの特定の場所で使用するのにBOARD_WIDTHが正しい定数にならない。
アレフゼロ

3
@Blrfl ...またはコードのオーバーエンジニアリングにつながります。将来変更される可能性のあるものを認識し、それに応じてコーディングするのは開発者の責任です。要件のようなコーディングは変更されませんが、問題は発生しますが、もう一方の極端な方法は改善されません。
マルコム

4
@corsiKaループ変数が物理軸に対応する場合、x、y、row、col、またはチェスの場合はfile、rankという名前にする必要があります。
user949300

11

OK、私が持っているいくつかのコメントがあります:

マジックナンバーを取り除くことは素晴らしいアイデアです。DRYと呼ばれる概念がありますが、これはしばしば誤って伝えられますが、アイデアはプロジェクトの概念の知識を複製しないことです。したがって、ChessBoardというクラスがある場合は、BOARD_SIZEまたはChessBoard.SIZEという定数をそれに添付しておくことができます。このように、この情報の唯一のソースがあります。また、これは後で読みやすくなります:

for (int i = 0; i < ChessBoard.SIZE; i++){
  for (int j = 0; j < ChessBoard.SIZE; j++){

数が変わらない場合でも、プログラムは間違いなく優れています。それを読んでいる人は誰でも、コードが何をしているかについての詳細を知っています。

悪い名前は名前がないよりも悪いですが、それは何かに名前を付けるべきではないという意味ではありません。名前を変更するだけです。お風呂の水で赤ちゃんを捨てないでください。:p名前は、それが何を説明しているかをよく理解している限り、説明的なものにすることができます。次に、その概念を複数の異なるものに使用できます。


8
これは、単にトップアンサーですでに作成され説明されたポイントを繰り返すようです
-gnat

-7

本当に必要なのは、名前が付けられているか裸であるかにかかわらず、定数への広告の吐き気の参照を排除することです:

for_each_chess_square (row, col) {
  /*...*/
}

このようなループなどを繰り返して定数を実際に増やす場合は、に固執することをお勧めします8

8自己記述的です。他の何かを表すマクロではありません。

You Ai n't Never Gonna(TM)は9x9のチェスプログラムに変えます。もしあなたがそうするなら、8の急増は大きな困難ではありません。

150,000行のコードベースでトークン8を検索し、どの発生が何を意味するかを数秒で分類できます。

もっと重要なのは、チェスの知識ができるだけ少ない場所に集中するようにコードをモジュール化することです。文字通り8が現れるチェス固有のモジュールを1つ、2つ、または3つ持つ方が、シンボリック名で8を参照するチェス固有の責任を伴う37個のモジュールよりも優れています。

この8定数がプログラムの緊張の原因になる場合、またはその場合、その時点で簡単に修正できます。現在発生している実際の問題を修正します。あなたがその特定の8によって妨げられていると感じないなら、その本能で行ってください。

将来、代替のボード寸法をサポートしたいとします。その場合、これらのループは、名前付き定数を使用するか8、またはを使用するかを変更する必要があります。ディメンションはboard.widthandなどの式によって取得されるためboard.heightです。のBOARD_SIZE代わりに持っている場合8、これらの場所は見つけやすくなります。それで、それはより少ない努力です。ただし、交換の手間を忘れてはならない8とのBOARD_SIZE最初の場所で。全体的な労力は少なくありません。コードを1回パスしてに変更8BOARD_SIZE、別のコードを使用し8て代替ディメンションをサポートすることは、単に代替ディメンションサポートに移行するよりも安くはありません。

これは、純粋に冷静な客観的なリスク/利益分析からも見ることができます。現在、プログラムにはベア定数があります。これらが定数に置き換えられた場合、利点はありません。プログラムは同一です。どんな変更でも、ゼロ以外のリスクがあります。この場合、それは小さいです。それでも、利益なしでリスクをとるべきではありません。この推論に直面して変化を「販売」するためには、利益を仮定する必要があります。これは、別のプログラムに役立つ将来の利益、つまり現在存在しないプログラムの将来のバージョンです。そのようなプログラムが計画されている場合、この仮説とそれに関連する推論は真正であり、真剣に受け止められるべきです。

たとえば、これらの定数をさらに増殖させるコードを追加するのに何日も離れている場合は、それらを廃止することができます。定数のそれらのインスタンスがこれまでに存在するほぼすべてのインスタンスである場合、なぜ気にしますか。

商用ソフトウェアで作業する場合、ROIの引数も適用されます。プログラムが販売されておらず、ハードコードされた数値を定数に変更しても販売が改善されない場合、その労力は補償されません。この変更は、時間の投資に対するリターンをゼロにします。ROIの議論はお金を超えて一般化します。あなたは時間と労力を費やしてプログラムを作成し、それから何かを得ました。それがあなたの「R」です。単独でその変更を行うことにより、それが何であれ、その「R」の多くを取得する場合、すべての手段によって。さらなる開発の計画があり、その変更により「R」が改善される場合も同様です。変更がすぐにまたは予測可能な「R」を持たない場合は、忘れてください。


13
8自己記述的ではなく、何を意味する数字でもあります。そして多分彼らは9x9のチェスゲームをしたいと思うのでしょうか?150,000行のコードがある8場合、コードのそれぞれの意味を記憶する方法はありません。できたとしても、なぜでしょうか。これは、多くの余分な手間です。モジュール化に関しては、モジュールの8ようなものに置き換えてもモジュール性をNUM_RANKS損なうことはありません。実際、コードをいじりやすくするので役立ちます。
Jerfov2

4
@カズ私もそれをします。そして、いくつかの使用法が私が思っていたものではなかったので、重要な奇妙なバグを紹介します。そのため、私はそもそもこのシナリオに入ることを避けているので、それを容認するアドバイスをサポートすることはできません。
-doppelgreener

1
「ただし、交換の手間を忘れてはならない8とのBOARD_SIZE最初の場所で」 -あなたは、それぞれが何を理解しようと、あなたのコードの上に精査費やしたのだ時間8今からあなたのプログラムの数ヶ月でするには、最も可能性の高い時間を超える置き換える過ごし8て意味のあるモジュールレベルの定数。
クリスチャンディーン

1
@doppelgangerそのシナリオはすでにここにあります。私は、定数を使用するチェスプログラムを取り、8に置き換えても、私は「計画は未書き込みのチェスプログラムで定数を使用していない」と言っています、とは言わないよ
カズ

1
@Kazなぜ8の周りのすべてのコードを読まなければならず、意味のある名前を与えるのに3秒余分に費やすだけでコンテキストを間違えることがあるのでしょうか?定数の実際の値を知るためとして、それははるかに簡単後方エンジニアにしようとするよりも、変数の正確な値を探しているものはコード手段でいくつかの魔法の整数
Jerfov2
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.