Search code examples
javaswingmodel-view-controllerjbutton

how to implement MVC pattern for word guessing game?


I have some working code for the word guessing game. But I fear it does not confine the design rules especially the MVC pattern. The attached image is my GUI currently. I am throwing around objects from one class to another and I hear that it is a bad style. while I agree with that, I am not able to come up with good MVC pattern approach for the word guessing game or the Hangman commonly called. The main application will have some like this:

public class Application {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                runApp();
            }

        });
    }

    public static void runApp() {
        Model model = new Model();
        View view = new View(model); //not sure if this correct, some suggest it is valid and some not

        Controller controller = new Controller(view, model);
    }

}

how would I approach this? The GUI as seen in the attached picture would be the View Class. This includes all JButtons, Textfield, borders, labels etc. Attach actionlisteners to JButtons in the View class

The controller will pass the events to the model. for example, if some letter buttons are clicked, it would pass that letter "A" is clicked to model and the model will either send instructions to controller to update view or it will update view directly. from my understanding of the MVC pattern, the model class must be implemented and tested separately from view and controller. I do not understand how I can achieve this here. I have complete code available. I need to refactor to confine to MVC pattern. kindly pass on your suggestions.enter image description here


Solution

  • I think one of the areas you are getting confused over is "responsibility". What is each component responsible for and what can it actually do.

    The problem isn't that you are passing Objects around you program, but more that the objects you are passing are exposing parts of your application that the recipient has no business knowing about or should be allowed to manipulate.

    What I mean by this is, if you were to pass the "buttons" panel to the "guess" panel, because you wanted to have the ability to allow the "guess" panel detect when a button was clicked, you've exposed the "buttons" panel to an area of your application that has no right to actually see it.

    What's stopping the "guess" panel from removing components? Nothing...

    Instead, we should use interfaces which determine what each part of the application can and can't do and what information is available to it.

    This is where you model comes in. The model determines what information is available, how it can be accessed and what events might be triggered to notify interested parties that the model has changed.

    For example. Your "buttons" panel would tell the model that the user has made another guess (in response to the user pressing the button). The model would then raise an event, which would notify the "guess" panel that a change has occurred. The "guess" panel then would update it's state accordingly, asking the model for the information it needed in order to represent the current state of the model (as far as it was responsible for).

    You could take a look at

    Now, with the MCV pattern, the view must be able to see the model, the controller must be able to see the view and model and the model doesn't care.

    The controller is listening for changes to the view (ie user interactions), which it passes to the model. The model fires notifications about changes to it's state and the view responds to those changes by updating itself as required.

    For example, the use clicks a button on the "button" panel. The "button" panel's controller detects this event (probably via an ActionListener), it process this action and updates the model.

    The model updates it's internal state and fires some kind of event.

    The "guess" panel detects this change in the model (via some kind of listener) and updates it's view accordingly (update the guess's and the image as dictated by the model).

    Now, remember, Swing doesn't use a pure MCV pattern, it's controls (ie buttons) are both the controller and the view, so just be careful when playing around with these...

    I would start with a HangManModel interface which defines all the properties you want to expose, such as the guesses, the "secret" word and perhaps the number of incorrect guesses made and the state of the game (win or lose) for example.

    I would also define the listeners that might be registered to the model, which describes the events that this model can generate. You could use a PropertyChangeListener or even a ChangeListener or define your own, based on your own needs, for example...

    public interface HangManModel {
    
        public void addGuess(char guess);
    
        public char[] getGuesses();
        public String getSecretWord();
        public int getState(); // running, win or lose
    
        public void addChangeListener(ChangeListener listener);
        public void removeChangeListener(ChangeListener listener);
    
    }
    

    Now this is just an example, personally, I might be tempered to hide the secret word and expose properties about it (like it's length for example). You could also be tempted to provide a setter for the secret word, so the model could be reset...

    This would represent the "heart" of your application, around this, you would build your views and controllers.