I'm doing a class diagram for a game project, but I'm stuck on a coupling problem.

Context

The projet is a turn-based game. Two users are playing. They've got units on a map. They play until 1 player has all his units dead or if a predefined number of turns are reached. At each round, each player will review his units one by one:

  • he can move an unit until the unit hasn't got any movement point left
  • he can skip the unit's turn

Modeling

Class diagram

Classes irrelevant to the questions have been cut

The Game class holds all the relevant objects : the players, the units, the current turn (which player(s) have already played, who's next, ...). The Turn object holds a PlayerTurn object, which keeps tracks of which units have already been played, which units are left to play, ...

Problem

-> Let's suppose we're creating the GetNextPlayableUnit method (in class PlayerTurn).

We'll have to iterate through the current player's units and find one that is not in the "PlayedUnits" list. Therefore, we'll have to call Game.Units.GetUnits with the parameter Game.Turn.CurrentPlayer (Game refers to the current Game's instance).

-> Let's suppose we're creating the MoveTo method (in class Unit).

If the Unit hasn't got any movement point left, we'll have to call Game.Turn.PlayerTurn.Finish which will either select the next playable unit or finish the current player's turn if he already played all of his units this turn.

-> ... There are a lot of other scenarios where we'll need to access other classes' method. E.g., a "Kill" method on Unit will have to check if the current player has still got at least 1 unit alive, and it will have to call a Finish method on Game otherwise.

Question

I think the coupling can't be avoided (correct me if I'm wrong though!). I've thought about two possible ways of dealing with coupling:

Solution 1

When constructing instances of these classes, I could pass the current Game's instance. After all, it makes sense: Unit instances, Turn instances, ... all belong to a game. Then, I could implement the methods GetNextPlayableUnit, MoveTo, Killed, ... easily by using this instance. Somehow, I don't feel comfortable sharing the current game's instance between all the model classes, even though I read here and there that it was the principle of dependency injection.

Solution 2

  1. Analyse the method dependencies: e.g., Kill method depends on Game.Units.Unit.Player (to retrieve the Player owning the Unit), on Game.Units.GetUnits (to find all other Units owned by the player), on Game.End to end the game if unit count of the player has reached 0.
  2. Find the dependencies common root: in the example above, the common root between Game.Units.Unit, Game.Units and Game is Game. In another example, if we had the dependencies Game.Units.Unit & Game.Units.SomethingElse, the common root would be Game.Units.
  3. Implement the method in this common root class. We're guaranteed not to need to access parent classes (in the association hierarchy).

Risks:

  • lots of methods will have the Game class for common root => lots of methods in Game class => lots of code in Game class.
  • methods won't belong to their rightful classes. Consider the method "KillUnit": you'd expect to find it in the Unit class directly, not in the Game class. Same goes for "MoveTo", "Finish", ...

Is there any other solution you can think of? What would be the best approach in this particular case?

Related posts

Recent Viewed