架空のゲーム エンジン API の関数の例を考えてみましょう。
function Entity.SetHealth( Number health )
-1
このような関数がパラメーターとして受け入れられ、この場合、エンティティが無敵になる原因となるのは悪いことですか?2 つの追加関数を使用する必要があります:
Entity.SetInvincible
とEntity.GetInvincible
?
無敵と健康に関するこの例は、実際には私が作成したものであることに注意してください。
架空のゲーム エンジン API の関数の例を考えてみましょう。
function Entity.SetHealth( Number health )
-1
このような関数がパラメーターとして受け入れられ、この場合、エンティティが無敵になる原因となるのは悪いことですか?
2 つの追加関数を使用する必要があります:Entity.SetInvincible
とEntity.GetInvincible
?
無敵と健康に関するこの例は、実際には私が作成したものであることに注意してください。
あなたができるもっと悪いことはたくさんあります、そして私はさまざまなタスクのための多くのゲームエンジンとUIフレームワークで非常に頻繁にその正確なアプローチを見ます(例えば、動きを繰り返すコマンドで、繰り返しを-1に設定することは意味します「永遠に繰り返す」)。他のすべてがほぼ正しい場合、このデザインの選択を批判するのはつまらないことです。
とは言っても、セマンティクスは非直感的 (-1 は無敵ですか?) なので、余分な関数を用意した方がよいでしょう。
通常、マジック ナンバーは悪い兆候ですが、禁止すべきだとまでは言いません。言い換えれば、-1
それ以外の場合は不正な値である場合、それを使用できますが、少なくとも定数を作成して、そのメソッドの呼び出しが次のようになるようにします。
someEntity.SetHealth(Health.Infinite)
または同様に、定数の命名はあなた次第です。
ただし、(私の意見では) より良い方法は、値をカプセル化して追加のデータを与えることです。たとえば、C# で次のような型を作成できます。
public struct Health
{
private readonly int _Value;
public int Value { get { return _Value; } }
public Health(int value)
{
if (value < 0 || value > SOME_ARBITRARY_MAX_NUMBER)
throw new ArgumentOutOfRangeException("value");
_Value = value;
}
public static Health Infinite
{
get
{
Health result = new Health(0);
result._Value = -1;
return result;
}
}
public bool IsInfinite
{
get
{
return _Value == -1;
}
}
}
次に、必要な比較メソッド、演算子なども追加して、たとえば次のことができるようにします。
Health a = Health.Infinite;
Health b = 100; // automatic type coercion
if (b < a) // custom operator, knows that Infinite > *
...