Search code examples
javaswingmodel-view-controllerobserver-pattern

How to update and view current window in MVC


I know this question has been covered in many posts here. However, something is still not clear to me, so i wanted to ask you my problem in detail.

I have to develop a java application using Swing and using the MVC model.
The application is mainly divided into two parts:

  • login part
  • questionnaire part (after login, a questionnaire is displayed)

So following MVC model i divided my code into 3 packages containing the following classes:

Model
        LoginModel
        QuestionModel
View
        LoginView
        QuestionView
Controller
        LoginController
        QuestionController

After developing these classes, i didn't know how to set the window that the program was current working on (login, questionnaire or other future implementations).
So i thought about implementing 3 other classes that use the Observer pattern:

MainModel - Observable
MainView
MainController - Observer

But now i'm not sure how to change the current window.

For example when login is successful, the window must change from LOGIN to QUESTION, so "MainModel.window = Window.QUESTION" and send it to the View.
Should it be added in LoginModel.login() by extending LoginModel with MainModel?
Or how can I do this?

My code:

public class main {
    public static void main(String[] args) {
        MainView view = new MainView();
        MainModel model = new MainModel();
        MainController controller = new MainController(view, model);
    }
}

public class MainView {
    private JFrame window;

    public MainView() {
        window = new JFrame();
        window.setLayout(new CardLayout(0, 0));

        LoginView login = new LoginView();      // init window at opening
        QuestionView question = new QuestionView();

        window.add(login);
        window.add(question);

        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setVisible(true);
    }
        
    public void update(Window window) {
        // ??
    }
}

public class MainModel {
    private List<Observer> observers = new ArrayList<>();
    private Window window;
    
    public MainModel() {
        window = Window.LOGIN;  // init window at opening
    }
    
    public void addObserver(Observer observer) {
        observers.add(observer);
    }
    
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }
    
    public void setWindow(Window newWindow) {
        newWindow = window;
                
        for (Observer o : observers)
            o.update(newWindow);            
    }
}

public class MainController implements Observer {
    private MainView view;
    private MainModel model;
    
    public MainController(MainView view, MainModel model) {
        this.view = view;
        this.model = model;
        
        this.model.addObserver(this);
    }
    
    @Override
    public void update(Window window) {
        this.view.update(window);
    }
}

public class LoginView extends JPanel {
    private JButton btnLogin;
    // ... other attributes
    
    public LoginView() {        
        btnLogin = new JButton("Login");

        new LoginController(this);
    }
        
    public JButton getBtnLogin() {
        return btnLogin;
    }
    
    public void ShowResult(boolean bResult) {
        // print result with JOptionPane.showMessageDialog
    }   
}

public class LoginController {
    private LoginView view;
    
    public LoginController(LoginView view) {
        this.view = view;
        setActionListener();
    }
    
    public void setActionListener() {
        ActionListener loginButton;
        loginButton = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                LoginModel model = new LoginModel();
                boolean bResult = model.login(view.getUserNameField(), view.getPasswordField());
                
                view.ShowResult(bResult);
            }
        };
        
        view.getBtnLogin().addActionListener(loginButton);
    }
}

public class LoginModel {
    // ... attributes etc
    
    public boolean login(String username, String password) {
        boolean bResult;
        
        // ...                  Some operation etc (useless for this example)
        bResult = true;     //  Simulation login successful
        
        if (bResult)
            // ? Change window model to Window.QUESTION.
            // But how?
            // LoginModel extends MainModel? To call "super.setWindow(Window.QUESTION)?
        
        return bResult;
    }
}

// My Observer class
public interface Observer {
    public void update(Window window);
}

// My Window class
public enum Window {
    LOGIN,
    QUESTION,
}

// Questionnaire classes code is very similar to the Login code
public class QuestionView extends JPanel {
    private JButton btn;
    // ...
    new QuestionController(this);
    // ...
}

public class QuestionController {
    private QuestionView view;
    // ...
    setActionListener();
    // ...
}

So in conclusion is it correct to use this approach? Or how else could i view/update the current window better?


Solution

  • In Swing, the MVC pattern looks like this:

    • The view reads from the model
    • The view may not update the model
    • The controller updates the model and the view

    The MVC name implies that you create the model first, then the view, then the controllers.

    There's usually not one controller to "rule them all". Each listener is responsible for its own part of the model and the view.

    You usually have one application model. An application model is made up of one or more plain Java getter/setter classes. In your case, it looks like a Person class and a Questionaire class. You would probably also have a Question class, to hold one question, several possible answers, and the chosen answer. You may have additional plain Java getter/setter classes I'm not thinking about now.

    You would have one JFrame, one JPanel to hold a question and possible answers, and a JDialog for the login and password. You may need multiple JPanels for different types of answers (not different questions), so you might need a main JPanel with a CardLayout.

    Your controllers will be the ActionListener for the login JButton, and the "I'm finished answering this question" JButton. You may have other listeners that I'm not thinking about now.