La prochaine étape dans la conception de notre application MVC consiste à implémenter notre contrôleur. Entrons maintenant dans le vif du sujet !
Concevez le contrôleur
Le contrôleur est responsable de l'enchaînement des interactions avec l'utilisateur. Après observation du jeu, l'enchaînement des séquences se révèle être le suivant :
Créer le jeu.
Entrer les noms des joueurs.
Mélanger les cartes.
Distribuer une carte à chaque joueur.
Retourner les cartes.
Déterminer le gagnant.
Afficher le gagnant.
Recommencer le jeu.
Le contrôleur doit savoir à quelle étape en est le jeu et recevoir une demande valide pour cette étape.
Comment le contrôleur est-il informé d'une demande ?
Une demande, ou commande d'entrée, vient de la vue. Le contrôleur évalue cette entrée. Toute entrée non valide est ignorée ou génère une exception de type erreur (vous décidez de la sévérité de l’erreur, et de la façon d'informer l'utilisateur de sa saisie non valide via la vue).
Nous devons à présent examiner l'enchaînement des étapes ci-dessus et identifier les interactions entre le contrôleur et la vue.
Démarrer le jeu
La première étape consiste à instancier le contrôleur. Le contrôleur instancie les objets essentiels au démarrage du jeu. Il s'agit du jeu de cartes et d'une liste vide de joueurs. Il doit également avoir connaissance de la vue. La vue doit être créée ailleurs et transmise au contrôleur au lieu d'être créée par le contrôleur lui-même.
Bon, mais pourquoi faire en sorte que la vue soit transmise au contrôleur, plutôt que de le laisser créer la vue lui-même ?
Eh bien, si demain, vous deviez créer une version mobile de cette application, les composants UI seraient différents, non ? Vous devriez donc modifier le contrôleur pour les créer. Et cela contreviendrait au numéro 2 des principes SOLID : le principe ouvert/fermé.
Entrer les noms des joueurs
Après chaque saisie d'un nom par l'utilisateur, le contrôleur ajoute le nom à la liste des joueurs.
Indiquer que la saisie des noms est complète
Le contrôleur demande à la vue de présenter l'état du jeu, c'est-à-dire les valeurs des divers objets.
Distribuer les cartes
Le contrôleur mélange les cartes et prend la première carte pour la donner à un joueur. Il demande ensuite à la vue de présenter l'état du jeu. Qui se compose désormais des noms des joueurs et d'une carte, face cachée, pour chaque joueur.
Révéler les cartes
Le contrôleur retourne la carte de chaque joueur, puis calcule le gagnant. Il demande à la vue de présenter l'état du jeu, qui comprend à présent le nom du gagnant ! Les mains des joueurs sont ramassées et replacées dans le paquet de cartes.
Rejouer
Le contrôleur retourne à l'étape 3.
Implémentez le contrôleur
Le contrôleur doit avoir des méthodes pour chacune des étapes d'interactions avec l'utilisateur. La vue appelle ces méthodes. Après chaque étape, le contrôleur informe la vue du nouvel état du jeu.
Écrivons ces méthodes ensemble !
public class GameController {
public GameController(View view, Deck deck) {}
public void addPlayer(String playerName) {}
public void startGame() {}
public void flipCards() {}
void evaluateWinner() {}
void displayWinner() {}
void rebuildDeck() {}
};
Examinons à présent ce que nous avons écrit.
Démarrer le jeu correspond à la View, au Deck et au GameController, instancié ailleurs (dans Main.java). Le GameController reçoit la vue et le jeu.
Entrer les noms des joueurs correspond à la méthode addPlayer.
Indiquer que la saisie du nom est complète est géré par la méthode startGame (une fois qu'un jeu est lancé, nous n'autorisons pas l'ajout de joueurs).
Distribuer les cartes est également géré par la méthode startGame, car c'est la première chose qui se passe au début du jeu.
Retourner/révéler les cartes est géré par la méthode flipCards. C'est également à ce moment que l'on calcule qui est le gagnant. De plus, la vue est appelée avec le nom du joueur gagnant.
Rejouer (rebuildDeck()) récupère toutes les cartes distribuées, et les remet dans le jeu, puis retourne à l'étape 3.
Concevez la vue
Rappelez-vous que la vue est à la fois l'interface utilisateur et le générateur d'événements. Le contrôleur appelle exclusivement les méthodes d'affichage de la vue. Par conséquent, vous devriez vous intéresser à ce que le contrôleur demande à la vue d'afficher. Et vous ne voulez pas que la vue accède directement aux informations du modèle (ce serait contraire au principe de la responsabilité unique). Au lieu de montrer un joueur, vous devez seulement montrer le nom d'un joueur. Il s'agit de la seule information devant être affichée à ce stade. Il en va de même pour la carte de jeu. Vous pouvez indiquer la valeur et la couleur à afficher, plutôt que la PlayingCard complète.
Faisons cela ensemble !
public class View {
public void showPlayerName(int playerIndex, String name);
public void showFaceDownCardForPlayer(int playerIndex, String name);
public void showCardForPlayer(int playerIndex, String Name, String rank, String suit);
public void showWinner (String winnerName);
};
Examinons ce que nous avons ajouté :
Entrer les noms des joueurs : la vue envoie la chaîne saisie au contrôleur, lequel rappelle la méthode showPlayerName de la vue.
Distribuer les cartes : appelle la méthode showFaceDownCardForPlayer de la vue.
Révéler les cartes : appelle les méthodes showCardForPlayer et showWinner de la vue.
MVC recommande que les saisies soient collectées par l'UI, puis envoyées au contrôleur, lequel, en retour, rappelle la vue :
Par exemple, après la saisie d'un nom, la méthode controller.addPlayer(playerNameString) est appelée. Le contrôleur rappelle la vue avec un nouvel appel showPlayerName(playerIndex, name).
Cela vous paraît un peu lourd ? Pourquoi la vue ne conserve-t-elle pas simplement les noms des joueurs ?
Ici, je ne peux pas hisser mon pavillon SOLID pour vous répondre. :) Les objets du modèle sont responsables de la gestion des données. Les objets Player connaissent déjà le nom qu'ils stockent (c’est leur responsabilité unique). Par conséquent, ils peuvent tout aussi bien conserver cette donnée en un seul endroit.
Implémentez la vue
Notre implémentation est simple. La vue présente seulement les noms des joueurs, les cartes distribuées et les résultats dans la console. Cependant, comme nous avons isolé cette fonctionnalité, nous pourrions facilement remplacer le mécanisme avec une implémentation GUI.
Faisons cela ensemble.
// implementation en ligne de commande simple
// La vue n'a pas besoin de garder une liste des joueurs.
// Cette méthode sera appellée par GameController à chaque fois qu'il faudra afficher le nom d'un joueur
public void showPlayerName(int playerIndex, String name) {
System.out.println("[" + playerIndex + "][" + name + "]");
}
Examinons le code. Nous disposons à présent d'une interface utilisateur simple qui affiche les informations que nous transmet le contrôleur.
Nous devons cependant retourner au contrôleur et appeler ces méthodes là où c'est nécessaire. Et nous devons relier tous les éléments ensemble dans une classe de jeu qui aura une méthode principale appelante.
En résumé
Le contrôleur a pour mission de réaliser l’enchaînement des étapes d'un cas d'utilisation et de valider les événements envoyés par la vue.
La vue est responsable de la présentation des informations du modèle et doit collecter les saisies effectuées par l'utilisateur.
Les responsabilités du contrôleur sont définies en examinant le workflow de l'application. Les responsabilités de la vue sont définies par ce que le contrôleur doit présenter à l'utilisateur.
Nous disposons à présent d'une application opérationnelle fidèle à l'approche MVC ! Dans la partie suivante, nous commencerons à appliquer les principes de conception SOLID un par un, pour rendre notre application plus robuste.