私が頻繁に遭遇する基本的な問題ですが、明確な解決策を見つけたことはありますが、共通の基本クラスまたはインターフェイスの異なるオブジェクト間の相互作用の動作をコーディングする必要がある場合です。少し具体的にするために、例を挙げます。
ボブは、「クールな地理的効果」をサポートする戦略ゲームのコーディングを行っています。これらは、部隊が水中を歩いている場合、25% 減速するなどの単純な制約に切り上げられます。草の上を歩いている場合は 5% 遅くなり、歩道を歩いている場合は 0% 遅くなります。
現在、経営陣はボブに、新しい種類の軍隊が必要だと言いました。ジープ、ボート、ホバークラフトもあります。また、彼らはジープが水に浸かった場合にダメージを受けるようにし、ホバークラフトは 3 つの地形タイプすべてを無視するようにしました。噂によると、ユニットの速度を落としてダメージを受けるだけでなく、さらに多くの機能を備えた別の地形タイプを追加する可能性もあります.
非常に大まかな疑似コードの例を次に示します。
public interface ITerrain
{
void AffectUnit(IUnit unit);
}
public class Water : ITerrain
{
public void AffectUnit(IUnit unit)
{
if (unit is HoverCraft)
{
// Don't affect it anyhow
}
if (unit is FootSoldier)
{
unit.SpeedMultiplier = 0.75f;
}
if (unit is Jeep)
{
unit.SpeedMultiplier = 0.70f;
unit.Health -= 5.0f;
}
if (unit is Boat)
{
// Don't affect it anyhow
}
/*
* List grows larger each day...
*/
}
}
public class Grass : ITerrain
{
public void AffectUnit(IUnit unit)
{
if (unit is HoverCraft)
{
// Don't affect it anyhow
}
if (unit is FootSoldier)
{
unit.SpeedMultiplier = 0.95f;
}
if (unit is Jeep)
{
unit.SpeedMultiplier = 0.85f;
}
if (unit is Boat)
{
unit.SpeedMultiplier = 0.0f;
unit.Health = 0.0f;
Boat boat = unit as Boat;
boat.DamagePropeller();
// Perhaps throw in an explosion aswell?
}
/*
* List grows larger each day...
*/
}
}
おわかりのように、Bob が最初からしっかりとした設計文書を持っていれば、状況は改善されたでしょう。ユニット数と地形タイプが増えるにつれて、コードの複雑さも増します。ボブは、どのメンバーをユニット インターフェイスに追加する必要があるかを考える必要があるだけでなく、多くのコードを繰り返さなければなりません。新しい地形タイプでは、基本的な IUnit インターフェイスから取得できるものからの追加情報が必要になる可能性が非常に高くなります。
別のユニットをゲームに追加するたびに、新しいユニットを処理するために各地形を更新する必要があります。明らかに、これは処理されるユニットのタイプを決定する醜い実行時チェックは言うまでもなく、多くの繰り返しになります。この例では特定のサブタイプへの呼び出しをオプトアウトしましたが、そのような種類の呼び出しを行う必要があります。例としては、ボートが陸地に衝突したときにプロペラが損傷することがあります。すべてのユニットにプロペラがあるわけではありません。
この種の問題が何と呼ばれているのかはわかりませんが、それは多対多の依存関係であり、切り離すのに苦労しています。カップリングをきれいにしたいので、ITerrain の各 IUnit サブクラスに何百ものオーバーロードがあるのは好きではありません。
この問題に関する何らかの光明が非常に求められています。おそらく、私は軌道から外れていると考えていますか?