IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Design Patterns Discussion :

[Etat] [Java] état des cases d'un jeu


Sujet :

Design Patterns

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    41
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2006
    Messages : 41
    Par défaut [Etat] [Java] état des cases d'un jeu
    Bonjour,

    Je suis en train de concevoir un petit jeu Tic Tac Toe dans le but de m'exercer à utiliser les patterns, à essayer de comprendre pourquoi dans tel ou tel cas il faut les utiliser ou pas. Pour l'instant, j'en suis à me demander si le State Pattern est vraiment utile pour distinguer une case marquée par un X/O d'une case vierge. Et s'il est (utile), est-ce la bonne méthode pour l'implémenter.

    Voilà les parties de codes concernées:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    public interface SquareState {
    	public static final SquareState BLANK_STATE = new BlankState();
    	public static final SquareState MARKED_STATE = new MarkedState();
    	
    	public void mark(Square square, Mark mark) throws IllegalStateException;
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    public class MarkedState implements SquareState {
    	
    	@Override
    	public void mark(Square square, Mark mark) throws IllegalStateException {
    		throw new IllegalStateException();
    	}
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class BlankState implements SquareState {
    
    	@Override
    	public void mark(Square square, Mark mark) throws IllegalStateException {
    		square.setMark(mark);
    		square.setState(SquareState.MARKED_STATE);
    	}
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class Square {
    	private SquareState state = SquareState.BLANK_STATE;
    	private Mark mark;
    	
    	public void mark(Mark mark) throws IllegalStateException {
    		state.mark(this, mark);
    	}
    	
    	public void setState(SquareState state) {
    		this.state = state; 
    	}
    	
    	public Mark getMark() {
    		return mark;
    	}
    	
    	public void setMark(Mark mark) {
    		this.mark = mark;
    	}
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class Player {
    	private Mark mark;
    	private Board board;
    		
    	public Player(Mark mark, Board board) {
    		this.mark = mark;
    		this.board = board;
    	}
    	
    	public void takeTurn() {
    		try {
    			Point point = new Point('a', '1');
    			board.markSquare(point, mark);
    		} catch (IllegalStateException e) {
    			System.err.println("Illegal ("+board.getSquaresLeft()+")");
    		}
    	}
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class Board {
    	private static final int MAX_ROWS = 3;
    	private static final int MAX_COLUMNS = 3;
    	private static final int MAX_SQUARES = MAX_ROWS*MAX_COLUMNS;
    	private Map<Point, Square> squares = new HashMap<Point, Square>();
    	private int squaresLeft = MAX_SQUARES;
    	
    	public Board() {
    		buildSquares();
    	}
    	
    	public void markSquare(Point point, Mark mark) throws IllegalStateException {
    		Square square = getSquare(point);
    		square.mark(mark);
    		--squaresLeft;
    	}
    
    	public int getSquaresLeft() {
    		return squaresLeft;
    	}
    }
    Il y a une ligne qui me dérange, c'est le "--squaresLeft". J'aurai voulu la placer dans la méthode mark() de l'état mais il aurait alors fallu passer en paramètre le plateau à la méthode mark() que la case qui elle même l'aurait passée à celle de l'état, du coup je me suis dit que lancer une exception serait plus judicieux. Mais est-ce la bonne façon de faire, j'attends vos avis avec impatience.

    Merci

  2. #2
    Membre éprouvé
    Profil pro
    Inscrit en
    Août 2006
    Messages
    89
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 89
    Par défaut
    Bonjour,

    Concernant l'utilisation ou pas d'un design pattern, je crois malheureusement qu'il n'y a pas de réponse universelle, ça dépend toujours du projet lui-même et de ses possibles évolutions.
    Donc c'est une bonne idée d'expérimenter son utilisation.

    Dans votre cas, je pense que l'utilisation du pattern Etat n'est pas nécessaire (pour les fonctionnalités actuelles en tout cas).
    Je pense qu'un booléen indiquant si la case est cochée (un membre 'isMarked' dans la classe 'Square' par exemple) est suffisant.

    Ce pattern deviendrait utile si le nombre de méthodes dépendantes de l'état (le nombre de méthodes dans la classe 'SquareState') venait à augmenter ou si les transitions entre états venaient à se complexifier.

    En revanche votre implémentation du pattern Etat a l'air correcte.


    Concernant la variable squaresLeft, c'est évidemment la classe 'Board' qui doit gérer ses modifications, pas à la classe 'Square' et encore moins à la classe 'SquareState' même indirectement.
    Donc modifier cette variable doit effectivement se faire dans la classe 'Board', en réponse à l'opération 'mark()' sur la case.
    Sur l'implémentation, je ne suis pas expert Java, donc je ne pourrais pas vraiment donner de conseil sur l'utilisation d'exceptions ou autres.
    Je pense en tout cas que l'implémentation actuelle est meilleure que celle suggérant de modifier la variable dans la méthode 'mark()' de l'état.

  3. #3
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Par défaut
    Pour la gestion des exceptions, il y a plusieurs écoles de pensée. Pour ma part, je considère les exceptions comme devant signaler seulement une rupture de contrat (erreur dans une pré-condition, un invariant ou une post-condition).

    Si l'appelant a respecté le contrat d'appel, il n'y a pas de raison de lui retourner une exception. Une valeur de retour parait plus appropriée.

    Pour le pattern Etat, je pense qu'il n'est vraiment intéressant que lorsque la gestion des états devient complexe. Typiquement lorsque la gestion par un switch/case est trop lourde, ou pas assez évolutive. Dans ce cas, le pattern Etat permet de séparer/isoler chaque comportement. Un peu a l'instar du pattern Bridge/Adapter pour la création.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    41
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2006
    Messages : 41
    Par défaut
    Merci pour vos réponses

    Je vais donc retirer l'utilisation du pattern dans ce cas et faire au plus simple avec une petit condition pour savoir si la case est déjà marquée ou non.

    Toujours concernant le state pattern, j'ai lu ici ou là qu'on l'utilisait aussi plus globalement au niveau du jeu. Par exemple, ici on l'utiliserait dans la classe TicTacToe qui gère la boucle générale donc le changement de tour, voir s'il y a un gagnant ou si le jeu est bloqué. Seulement, je ne comprends pas trop comment m'y prendre pour l'implémenter. Quels états y aurait-il alors ?

  5. #5
    Membre éprouvé
    Profil pro
    Inscrit en
    Août 2006
    Messages
    89
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 89
    Par défaut
    Bonjour,

    Merci pseudocode pour vos informations éclairées!

    Gaspoute, je pense que votre problème vient du fait que vous essayez d'implémenter un Tic Tac Toe autour d'un pattern Etat, au lieu d'implémenter un Tic Tac Toe tout court.
    Je sais que ce n'est pas votre but à la base, mais je pense que c'est la seule façon de bien identifier de quels états vous aurez besoin.

    Lorsque vous identifiez dans votre analyse que telle ou telle partie de votre programme/fonctionnalité doit se comporter différemment selon certaines conditions ou contraintes, le pattern Etat peut probablement être utilisé. Il est alors facile d'identifier les états : un par comportement.

    En gros, la première étape est de répondre à la question 'Qu'est-ce qui peut être décomposé en états ?', et non pas 'quels sont les états ?'.
    Une fois cette première étape franchie, la réponse à votre question sera évidente.

    Sinon concernant cette partie :
    le changement de tour, voir s'il y a un gagnant ou si le jeu est bloqué
    Attention à ne pas vous perdre entre les notions d'états et de transitions entre états.
    Là vous ne décrivez pas d'états, mais des transitions entre états.

    Dans votre première implémentation, vos états étaient 'Blank' et 'Marked', pas 'c'est la première fois que la case est utilisée' ou 'la case cliquée est invalide'.
    Votre première implémentation avait donc deux états 'Blank' et 'Marked', avec une transition 'le joueur clique sur la case'.

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    41
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2006
    Messages : 41
    Par défaut
    OK merci

    Les états du jeu global seraient plutôt "fini" et "en cours". Dans ce cas, pour que l'état soit "fini", la transition serait soit parce que le jeu est bloqué soit parce qu'il y a un gagnant ?

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2006
    Messages
    41
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Septembre 2006
    Messages : 41
    Par défaut
    Pour l'instant j'en suis à ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    public class Game {
    	private GameState state = GameState.JOINING;
    	private Map<Session, Player> players = new HashMap<Session, Player>();
    	private Iterator<Player> iterator;
    	private int expectedPlayerCount;
    
    	public Game(int expectedPlayerCount) {
    		this.expectedPlayerCount = expectedPlayerCount;
    	}
    
    	public void joinPlayer(Session session) {
    		players.put(session, new Player(session));
    		--expectedPlayerCount;
    		if (expectedPlayerCount == 0) {
    			playGame();
    		}
    	}
    
    	public void playGame() {
    		setState(GameState.IN_PROGRESS);
    		playRound();
    	}
    
    	public void playRound() {
    		iterator = players.values().iterator();
    	}
    
    	public void playTurn(Session session) {
    		Player player = players.get(session);
    		player.playTurn(this);
    	}
    
    	public void changeTurn() {
    		if (isOver()) {
    			endGame();
    		} else {
    			if (iterator.hasNext()) {
    				Player player = iterator.next();
    				player.takeTurn();
    			} else {
    				playRound();
    			}
    		}
    	}
    
    	public void endGame() {
    		setState(GameState.ENDED);
    	}
    
    	public void setState(GameState state) {
    		this.state = state; 
    	}
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public class Player {
    	private PlayerState state = PlayerState.WAITING_FOR_TURN;
    	private Session session;
    
    	public Player(Session session) {
    		this.session = session;
    	}
    
    	public void takeTurn() {
    		setState(PlayerState.PLAYING);
    	}
    
    	public void playTurn(Game game) {
    		if (getState() == PlayerState.PLAYING) {
    			/* Play his turn */
    			setState(PlayerState.WAITING_FOR_TURN);
    			game.changeTurn();
    		}
    	}
    
    	public void setState(PlayerState state) {
    		this.state = state;
    	}
    
    	public PlayerState getState() {
    		return state;
    	}
    }
    Game est une super-classe. Une série de jeux à plateau hériteront de cette classe. La méthode playTurn() dépendera du jeu, par exemple pour les dames, ça sera move(int x, int y). Je voulais juste avoir un aperçu. J'ai utilisé des enum pour les états, étant donné qu'avec l'héritage ça complique un peu les choses. Notamment si chaque jeu a des méthodes différentes pour jouer un tour.

    Les méthodes accessibles sont donc joinPlayer() et playTurn(), qui changera de signature selon le jeu, ici c'est juste à titre d'exemple.

    Je ne sais pas si dans ce contexte, vous voyez le problème que j'ai à cause des états et de l'héritage.

    Merci

Discussions similaires

  1. Java 7 : petit état des lieux du projet Lambda...
    Par adiGuba dans le forum Général Java
    Réponses: 2
    Dernier message: 28/08/2010, 10h44
  2. Memoriser l'etat des cases a cocher d'un formulaire avec pagination
    Par belaggoun2000 dans le forum Général JavaScript
    Réponses: 8
    Dernier message: 26/02/2010, 15h23
  3. [Dojo] Grid: sauvegarde de l'état des case à cocher du menu "PlaceHolder"
    Par laminfodev dans le forum Bibliothèques & Frameworks
    Réponses: 1
    Dernier message: 27/11/2009, 01h15
  4. Jeu 2D avec des cases
    Par Zoloom dans le forum Interfaces Graphiques en Java
    Réponses: 5
    Dernier message: 08/02/2008, 17h33
  5. taille dynamique des cases dans état
    Par exter666 dans le forum Access
    Réponses: 14
    Dernier message: 09/09/2005, 15h30

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo