OO design et dépendances circulaires

Je suis actuellement aux sockets avec un problème de dépendance circulaire lors de la conception de mes cours.

Depuis que j’ai lu des informations sur le modèle de domaine anémique (ce que je faisais tout le temps), j’ai vraiment essayé de m’éloigner de la création d’objects de domaine qui n’étaient que des “seaux de getters et de setters” et qui retournaient à mes racines OO.

Cependant, le problème ci-dessous est un problème que je rencontre beaucoup et je ne suis pas sûr de savoir comment je devrais le résoudre.

Disons que nous avons une classe d’ équipe qui a beaucoup de joueurs . Peu importe de quel sport il s’agit 🙂 Une équipe peut append et supprimer des joueurs, de la même manière qu’un joueur peut quitter une équipe et en rejoindre une autre.

Nous avons donc l’équipe qui a une liste de joueurs:

public class Team { private List players; // snip. public void removePlayer(Player player) { players.remove(player); // Do other admin work when a player leaves } } 

Ensuite, nous avons le joueur, qui a une référence à l’équipe:

 public class Player { private Team team; public void leaveTeam() { team = null; // Do some more player stuff... } } 

On peut supposer que les deux méthodes (retirer et quitter) ont une logique spécifique à un domaine qui doit être exécutée chaque fois qu’une équipe retire un joueur et qu’un joueur quitte une équipe. Par conséquent, ma première pensée est que lorsqu’une équipe donne un coup de pied à un joueur, removePlayer (…) doit également appeler la méthode player.leaveTeam () …

Mais alors que se passe-t-il si le joueur est en train de diriger le départ – la méthode leaveTeam () doit-elle appeler team.removePlayer (this)? Non sans créer une boucle infinie!

Dans le passé , je venais de créer des POJO “stupides” pour ces objects et de faire en sorte qu’une couche service soit utilisée. Mais même maintenant, je rest avec ce problème: pour éviter les dépendances circulaires, la couche service a toujours le lien entre eux – c’est-à-dire

 public class SomeService { public void leave(Player player, Team team) { team.removePlayer(player); player.leaveTeam(); } } 

Est-ce que je complique trop cela? Peut-être me manque-t-il un défaut de conception évident. Tous commentaires serait grandement apprécié.


Merci à tous pour les réponses. J’accepte la solution de Grodriguez car elle est la plus évidente (je ne peux pas croire que cela ne m’est pas arrivé) et facile à mettre en œuvre. Cependant, DecaniBass fait valoir un bon point. Dans la situation que je décrivais, il est possible pour un joueur de quitter une équipe (et de savoir s’il fait partie d’une équipe ou non) ainsi que pour l’équipe qui effectue le retrait. Mais je suis d’accord avec votre sharepoint vue et je n’aime pas l’idée qu’il y ait deux “points d’entrée” dans ce processus. Merci encore.

Vous pouvez briser la dépendance circulaire en ajoutant des gardes pour vérifier si l’équipe a toujours le joueur ou si le joueur est toujours dans l’équipe. Par exemple:

En classe Team :

 public void removePlayer(Player player) { if (players.contains(player)) { players.remove(player); player.leaveTeam(); // Do other admin work when a player leaves } } 

En classe Player :

 public void leaveTeam() { if (team != null) { team.removePlayer(this); team = null; // Do some more player stuff.. } } 

Ben

Je commencerais par demander si un joueur peut (logiquement, légalement) se retirer de l’équipe. Je dirais que l’object joueur ne sait pas dans quelle équipe il est (!), Il fait partie d’une équipe. Supprimez donc le Player#leaveTeam() et effectuez tous les changements d’équipe par le biais de la méthode Team#removePlayer() .

Dans le cas où vous n’avez qu’un joueur et que vous devez le retirer de son équipe, vous pouvez utiliser une méthode de recherche statique sur l’équipe public static Team findTeam( Player player ) ...

Je sais que c’est moins satisfaisant et naturel qu’une méthode Player#leaveTeam() , mais d’après mon expérience, vous pouvez toujours avoir un modèle de domaine significatif.

Les références à 2 voies (parent -> enfant et enfant -> parent) sont souvent chargées d’autres choses, par exemple, le ramassage des ordures, le maintien de “l’intégrité référentielle”, etc.

Le design est un compromis!

L’idée est de faire des choses liées au domaine dans différentes méthodes qui ne s’appellent pas, mais font des choses liées au domaine pour leur propre object, c’est-à-dire que la méthode de l’équipe le fait pour l’équipe et celle du joueur le fait pour le joueur

 public class Team { private List players; public void removePlayer(Player player) { removePlayerFromTeam(player); player.removeFromTeam(); } public void removePlayerFromTeam(Player player) { players.remove(player); //domain stuff } } public class Player { private Team team; public void removeFromTeam() { team = null; //domain stuff } public void leaveTeam() { team.removePlayerFromTeam(this); removeFromTeam(); } } 
 public void removePlayer(Player player) { if (players.contains(player)) { players.remove(player); player.leaveTeam(); } } 

Idem dans leaveTeam .