Question

Cela fait un moment que je réfléchis à cette question de design orienté objet et que je ne parviens pas à trouver une solution satisfaisante, je me suis donc dit que je devrais y jeter un coup d'oeil.

J'ai une classe Jeu qui représente un jeu de plateau au tour par tour. Nous pouvons donc supposer qu'il est similaire à Monopoly aux fins de cette question. Dans ma conception, j'ai une classe Player contenant une méthode TakeTurn .

Le jeu parcourt tous les joueurs et appelle la méthode TakeTurn pour effectuer toutes les opérations nécessaires à la fin du tour. Je veux pouvoir avoir un nombre n de joueurs et pouvoir en définir un nombre arbitraire pour être des joueurs sur ordinateur. Donc, je pensais avoir une classe HumanPlayer et une classe ComputerPlayer , qui dérivent toutes deux de Player.

Le jeu ne connaît que la classe Player et appelle simplement la méthode TakeTurn sur chaque Player . Mon problème vient du fait que les objets ComputerPlayer peuvent être complètement automatisés, c’est-à-dire qu’ils conservent l’exemple de Monopoly et peuvent décider d’acheter une propriété en utilisant un élément de logique. Désormais, avec l’objet HumanPlayer , il doit obtenir une entrée de l’utilisateur réel pour pouvoir acheter une propriété, par exemple, ce qui semble impliquer une interface différente et potentiellement signifier qu’il ne devrait pas dériver

Je n'ai pas réussi à trouver une bonne solution au problème sans que la classe Jeu connaisse de manière explicite les implémentations réelles des différentes classes Player . Je pourrais toujours supposer dans la classe Jeu qu'il n'y aura jamais que des joueurs humains et des ordinateurs et le fermer efficacement pour une extension, mais cela ne semble pas être une bonne programmation OO.

Tout avis à ce sujet serait apprécié.

Était-ce utile?

La solution

Je pense que vous ne devriez pas laisser la classe Game gérer les opérations d'entrée-sortie. De cette façon, la méthode TakeTurn (bloquante) cachera au tableau les moyens de mise en œuvre. il peut utiliser d'autres objets pour communiquer avec l'utilisateur.

Tout ce que la classe de jeu devrait se préoccuper est l’état du plateau et du tour. les joueurs doivent tous implémenter une interface à un seul joueur et masquer toute implémentation du jeu.

Autres conseils

Si le jeu gère l'état du jeu et en cours d'E / S, le jeu en fait trop.

Vous voulez que Game soit étroitement concentré sur des règles, des tournants et des changements d'état. Le jeu ne sait pas ce qu'est un joueur. il sait seulement qu'il a des joueurs.

Vous voulez que les joueurs examinent l’état de la partie et exécutent les actions légales pendant leur tour.

Les joueurs humains et le jeu dans son ensemble Les deux partagent un package d'E / S commun qui indique l'état du jeu et invite les utilisateurs à donner leur avis.

Vous pouvez utiliser le Observable de Java en faisant du package d'E / S un Observer du jeu. De cette façon, les modifications de l'état du jeu sont signalées à l'E / S pour l'affichage ou la journalisation, ou les deux.

Je n'aurais probablement pas deux classes HumanPlayer et ComputerPlayer , mais une seule classe Player configurée au moment de la création avec la bonne entrée stratégie.

La façon dont le joueur obtient des informations pour décider de son coup au prochain tour de jeu est la seule chose qui varie (à partir de la description du problème original, au moins), donc encapsulez simplement cela dans un fichier. abstraction séparée.

Quelle que soit la classe de haut niveau qui configure le jeu, vous devez également créer les deux ensembles de joueurs (un humain, un autre simulé par ordinateur), avec la stratégie de saisie appropriée pour chacun, puis simplement donner ces objets de joueur à l'objet de jeu. . La classe Game n'appellera alors que la méthode TakeTurn de la liste de joueurs donnée, pour chaque nouveau tour.

Au lieu de dire à la classe de jeu qu’il n’ya qu’un seul être humain, pourquoi ne pas lui laisser cette entrée lors du menu / initialisation du jeu? S'il y a plus de joueurs, cela peut être décidé via une forme de saisie (sélectionner les joueurs dans le menu), avant l'initialisation de la classe de jeu.

L’interface que Player présente à Jeu est orthogonale au comportement des classes Player dérivées.

Le fait que l'implémentation de TakeTurn varie en fonction du type concret de l'objet Player ne doit pas être une cause d'inquiétude.

Je pense que la classe Game ne devrait concerner aucune implémentation des classes Player et ignorer l'interface utilisateur.

Toute entrée utilisateur doit être traitée par la classe HumanPlayer .

Je dirais que la classe Jeu ne devrait pas se soucier de savoir s’il s’agit d’un lecteur informatique ou d’un joueur humain. Il doit toujours appeler TakeTurn sur la classe de joueurs suivante. S'il s'agit d'un joueur humain, la classe Player a la responsabilité de communiquer avec l'utilisateur et de lui demander quoi faire. Cela signifie qu'il bloquera jusqu'à ce que l'utilisateur se décide. L'interaction de l'interface utilisateur ayant généralement lieu dans le thread principal d'une application, il est important qu'un TakeTurn bloquant ne bloque pas l'application dans son ensemble, faute de quoi les entrées utilisateur ne peuvent pas être traitées lorsque Jeu attend TakeTurn .

Au lieu de la partie , appelez TakeTurn sur tous les joueurs que les joueurs doivent appeler TakeTurn . dans les jeux et dans les jeux , vous devez valider si le bon joueur joue son tour.

Cela devrait permettre de résoudre le problème des lecteurs Utilisateurs et Ordinateurs .

Je ne sais pas si c'est ce que vous voulez

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
  }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top