views:

471

answers:

9

I've been thinking about this object oriented design question for a while now and have unable to come up with a satisfactory solution, so thought I'd throw it open to the crowds here for some opinions.

I have a Game class that represents a turn based board game, we can assume it's similar to Monopoly for the purposes of this question. In my design I have a Player class containing a method TakeTurn.

The Game loops through all Players and calls the TakeTurn method to do all the necessary things to complete the turn. I want to be able to have n number of players, and be able to set an arbitrary number of them to be computer players. So, my thought was to have a HumanPlayer class and a ComputerPlayer class, both of which derive from Player.

The Game knows only the Player class and simply calls the TakeTurn method on each Player in turn. My problem comes in the fact that ComputerPlayer objects can be completely automated, i.e. keeping with the Monopoly example, can decide to buy a property using some piece of logic. Now, with the HumanPlayer object, it needs to get an input from the actual user to be able to buy a property for instance, which seems to imply a different interface and potentially mean they shouldn't derive

I haven't been able to come up with a good solution to the problem without having the Game class know the actual implementations of the various Player classes explicitly. I could always make the assumption in the Game class that there will only ever be human and computer players and effectively close it for extension, but it doesn't seem like good OO programming.

Any opinions on this would be appreciated.

+5  A: 

I think you should not let the Game class handle IO. this way, the (blocking) TakeTurn method will hide from the game board the means of implementation. it can use other objects to communicate with the user.

all the Game class should concern itself with is the state of the board and the turn. the players should all implement a single Player interface, and hide all implementation from the Game.

Amir Arad
+1  A: 

Instead of telling the game class there is only ever one human, why not let it get that input during the menu/initialization of the game? If there are more players, that can be decided via some form of input (select players in the menu), prior to the game class initialization.

f4nt
A: 

I think the Game Class should not concern about any implementations of the Player classes, and also ignore the User Interface.

Any user input needs to be handled by the HumanPlayer class.

Grad van Horck
+1  A: 

The interface that Player presents to Game is orthogonal to the behaviour of derived Player classes.

The fact that the implementation of TakeTurn varies depending on the concrete type of the Player object should not be a cause for concern.

Seb Rose
A: 

Im am not sure if this is what you want

public abstract class Player 
{
  int position;
  DecisionMaker decisionDependency;

  ...

  public void TakeTurn()
  {
    position += RollDice();
    GameOption option GetOptions(position);
    MakeDescion(option);
  }

  protected int RollDice()
  {
    //do something to get the movement
  }

  protected abstract void MakeDecision(GameOption option);

}

Public class ComputerPlayer : Player
{
  public ComputerPlayer()
  {
    decisionDependency = new AIDecisionMaker();
  }

  protected override void void MakeDecision(GameOption option)
  {
    decisionDependency.MakeDecision(option);
    //do stuff, probably delgate toan AI based dependency
  }
}

Public class HumanPlayer : Player
{
  public HumanPlayer()
  {
    decisionDependency = new UIDecisionMaker();
  }

  protected override void void MakeDecision(GameOption option)
  {
    decisionDependency.MakeDecision(option);
    //do stuff, probably interacting with the a UI or delgate to a dependency
  }
}
RhysC
Please indent your code, it is difficult to understand.
Brad Gilbert
A: 

I'd say, the Game class shouldn't care if this is a computer player or a human player. It should always call TakeTurn on the next player class. If this is a human player, it is the responsibility of the Player class, to communicate with the user and ask the user what to do. That means it will block till the user made up his mind. As usually UI interaction takes place in the main thread of an application, it is only important that a blocking TakeTurn won't block the application as a whole, otherwise user input cannot be processed while Game waits for TakeTurn.

Mecki
+2  A: 

If the Game is managing the game state and doing I/O, the Game is doing too much.

You want Game to be tightly focused on just rules and turns and state changes. Game doesn't know what a player is; it only knows that it has players.

You want Players to examine the Game state and execute the legal actions during their turns.

Human Players and the Game as a whole both share a common I/O package that shows game state and prompts humans for their input.

You can make good use of the Java Observable by making the I/O package an Observer of the Game. That way, Game state changes are reported to the I/O for display or logging or both.

S.Lott
A: 

Instead of the Game class calling TakeTurn on all the players the players should call TakeTurn on the Game class and the Game class should validate if the right player is taking his turn.

This should help solve the User and Computer player problem.

Hemanshu Bhojak
+1  A: 

I would probably not have two HumanPlayer and ComputerPlayer classes, but a single Player class which is configured at creation time with the proper input strategy.

The way the player obtains information to decide its move in the next turn of the game is the only thing that varies (from the original problem description, at least), so just encapsulate that in a separate abstraction.

Whatever high-level class that sets up the game should also create the two sets of players (one human, another computer-simulated), with the proper input strategy for each, and then simply give these player objects to the game object. The Game class will then only call the TakeTurn method on the given list of players, for each new turn.

Rogerio