web-dev-qa-db-ja.com

モデルクラスの結合を管理する方法

ゲームプロジェクトのクラス図を作成していますが、結合の問題で立ち往生しています。

環境

プロジェクトはターンベースのゲームです。 2人のユーザーが遊んでいます。彼らは地図上にユニットを持っています。それらは、1人のプレイヤーがすべてのユニットを死ぬまで、または事前定義されたターン数に達するまでプレイします。各ラウンドで、各プレイヤーは自分のユニットを1つずつ確認します。

  • ユニットに移動ポイントがなくなるまで、ユニットを移動できます。
  • 彼はユニットのターンをスキップすることができます

モデリング

Class diagram

質問に関係のないクラスはカットされました

ゲームクラスは、関連するすべてのオブジェクトを保持します:プレーヤー、ユニット、現在のターン(どのプレーヤーがすでにプレイしたか、次は誰かなど)。 TurnオブジェクトはPlayerTurnオブジェクトを保持します。このオブジェクトは、どのユニットがすでにプレイされているか、どのユニットがプレイされているかを追跡します。

問題

-> GetNextPlayableUnitメソッド(PlayerTurnクラス)を作成しているとしましょう。

現在のプレイヤーのユニットを繰り返し処理し、「PlayedUnits」リストにないユニットを見つける必要があります。したがって、パラメーターGame.Turn.CurrentPlayerを指定してGame.Units.GetUnitsを呼び出す必要があります(Gameは現在のゲームのインスタンスを参照します)。

-> MoveToメソッド(クラスUnit内)を作成しているとしましょう。

ユニットに移動ポイントが残っていない場合は、Game.Turn.PlayerTurn.Finishを呼び出す必要があります。これにより、次のプレイ可能なユニットを選択するか、現在のプレイヤーがこのターンにすべてのユニットをプレイしている場合はそのターンを終了します。

-> ...他のクラスのメソッドにアクセスする必要があるシナリオは他にもたくさんあります。たとえば、ユニットの「キル」メソッドは、現在のプレーヤーがまだ少なくとも1つのユニットを生きているかどうかを確認する必要があり、そうでない場合はゲームのフィニッシュメソッドを呼び出す必要があります。

質問

カップリングは避けられないと思います(間違っていたら訂正してください!)。カップリングを処理する2つの可能な方法について考えました。

解決策1

これらのクラスのインスタンスを作成するときに、現在のゲームのインスタンスを渡すことができます。結局のところ、それは理にかなっています:ユニットインスタンス、ターンインスタンス、...すべてがゲームに属しています。次に、このインスタンスを使用して、メソッドGetNextPlayableUnit、MoveTo、Killed、...を簡単に実装できます。どういうわけか、依存性注入の原則であることをあちこち読んだとしても、現在のゲームのインスタンスをすべてのモデルクラス間で共有することに不安を感じています。

解決策2

  1. メソッドの依存関係を分析します。たとえば、Killメソッドは、Game.Units.Unit.Player(ユニットを所有するプレーヤーを取得するため)、Game.Units.GetUnits(プレーヤーが所有する他のすべてのユニットを検索するため)、Game.Endに依存します。プレイヤーのユニット数が0に達した場合にゲームを終了します。
  2. 依存関係を見つける共通ルート:上記の例では、共通ルート Game.Units.Unit、Game.Units、およびGame isGameの間です。別の例では、依存関係Game.Units.UnitとGame.Units.SomethingElseがある場合、共通ルートはGame.Unitsになります。
  3. このcommon rootクラスにメソッドを実装します。 (関連付け階層内の)親クラスにアクセスする必要がないことが保証されています。

リスク:

  • 多くのメソッドには、共通ルート=>ゲームクラスの多くのメソッド=>ゲームクラスの多くのコードのゲームクラスがあります。
  • メソッドは正当なクラスに属しません。メソッド「Kill​​Unit」について考えてみます。これは、Gameクラスではなく、Unitクラスで直接見つけることができます。 「MoveTo」、「Finish」、..も同様です。

あなたが考えることができる他の解決策はありますか?この特定の場合の最良のアプローチは何でしょうか?

2
Maxime

あなたは他の解決策について尋ねます。これらは、解決策を見つけるプロセスを反映している可能性があります。

  1. これはダイアグラムの最初のバージョンであり、破棄して再起動する必要はありません。それはあなたが古い、おそらく無関係な考えを新しい考えと混ぜる可能性を高めます。
  2. モデル化する間、それを実装することを考えます。それは、モデルがどうあるべきか、そしてプログラミング言語がそれをどのように見せたいかを混ぜ合わせたものです。モデリングは実装されていません。最終的には、モデルよりも多くのメソッドと、できれば実装内のクラスが増えるはずです。
  3. Unitsクラスは、目的の動作を実装する1つの方法だと思います。それはあなたの粒度のレベルで出すことができます。たとえば、PlayerクラスはStringサブクラスPlayerNameを指していません。ダイアグラムで1レベルの粒度を維持します。
  4. クラス図から始めるのは選択できないかもしれません。オブジェクト図はより具体的です。クラス図はより一般的です。したがって、意味をなさなくてもよい一般化で迷子になる可能性があると思います。
  5. さまざまなシナリオ、プレイスルーがあります。それらからいくつかのオブジェクト図を作成し、そこから一般化することができます。すべてを一度に1つの図に入れる必要はありません。

懸念事項:動作は、同時にいくつかのメソッドUnit.Die()Playfield.RemoveUnit()Player.KillUnit()で見つけることができます。それは大丈夫です。ただし、コードをコピーしてすべてのメソッドに貼り付けることは避けたいと考えています。

カップリングについて。私はあなたがカップリングを避けない/できる/しなければならない/したくないと思います。一部のオブジェクトは相互作用する必要があります。悪いケースは、すべてのオブジェクトがすべてのメソッドで常に他のすべてのオブジェクトと相互作用する場合です。それは、 関心の分離 のない結合が多すぎます。

そしておそらく時代遅れの意見:OOPはオブジェクトとその相互作用についてであり、クラスについてではなく、継承についてではありません。

2
User