0

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?

Anyone97
  • 65
  • 4
  • Take a look at this [Stack Overflow answer](https://stackoverflow.com/questions/65297470/i-have-a-problem-using-jframe-to-print-a-table-for-multiple-arrays/65484936#65484936). It's a student quiz, not a questionnaire, but it should be close enough to give you a good model to follow. The text of the GUI is Spanish. – Gilbert Le Blanc Oct 04 '21 at 14:05

1 Answers1

1

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.

Gilbert Le Blanc
  • 50,182
  • 6
  • 67
  • 111
  • So it's wrong to use "MainModel, MainView etc" because they don't make sense (they just storing the current window). About application model in the Model i've setter/getter classes and View is populated with the Model data returned via the controller. But if each controller is responsible for its own part of View/Model, how can i hide one View and show another? For example, after successful login, LoginController can send a method to LoginView to hide it. But at the same time how can i show QuestionView? Since Login and Questionaire have nothing in common and they don't know each other. – Anyone97 Oct 04 '21 at 11:38
  • 1
    @Anyone97: As I mentioned in my answer, you use a JDialog for the login and a main JPanel with a CardLayout for the Questionaire JPanels. [How can I implement a login screen before showing a JFrame?](https://stackoverflow.com/questions/7323086/java-swing-how-can-i-implement-a-login-screen-before-showing-a-jframe) [How to Use CardLayout](https://docs.oracle.com/javase/tutorial/uiswing/layout/card.html). – Gilbert Le Blanc Oct 04 '21 at 13:49