1

I'm currently creating an application in Java with Swing and using the MVC pattern, however I've run into somewhat of a concern.

The problem I'm facing is that the application will have multiple panels, such as a panel for logging in etc. So I decided to do the logical thing and use a Card Layout. However what I would like to do is have a model, view and controller for each panel so the code is tidier.

For example I would like to have a view, model and controller for the login panel. This works fine but what I can't seem to be able to achieve elegantly is changing the panel on the card layout when required e.g. When a user has logged in successfully there is no longer a need to display the login panel.

One solution I came up with (using an idea from this topic: How can I switch between JPanels in a CardLayout using MVC?) was to use a model for the system, which contained the possible states of the system. This would allow the class containing the Card layout to listen for any changes in the system state model. However this would mean that controllers such as the one used for logging in would have to interact with this system model (in order for the Card layout to be changed) as well as the model for logging in (to verify user credentials). I'm not sure if a controller referencing multiple models is good practice in terms of MVC.

Hopefully what I'm trying to achieve has been made clear, any help is appreciated.

View.java

package test;

import java.awt.CardLayout;
import java.awt.Container;
import javax.swing.JFrame;

/*
 * Will listen for changes in the model's states
 * and update cards accordingly.
 */

public class View {

    private JFrame frame;
    private CardLayout cards;

    public View(){
        cards = new CardLayout();

        frame = new JFrame();
        frame.setSize(500, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setVisible(true);

        frame.getContentPane().setLayout(cards);
    }
    public Container getContentPane(){
        return frame.getContentPane();
    }
    public static void main(String[] args) {
        View view = new View();
        Model stateModel = new Model();
        Controller controller = new Controller(stateModel, view);

        LoginView loginView = new LoginView();
        LoginModel loginModel = new LoginModel();
        LoginController loginController = new LoginController(stateModel,   loginModel, loginView);

        view.getContentPane().add(loginView.getPanel(),  Model.States.STATE1.toString());
        view.cards.show(view.getContentPane(), Model.States.STATE1.toString());

    }

}

Model.java

package test;

/*
 * Notify's listeners when the state has been changed.
 * 
 */

public class Model {

    public String currentState = "";

    public Model(){
        //Default state.
        this.currentState = States.STATE1.toString();
    }

    public void changeState(States state){
        currentState = state.toString();
        System.out.println("State has been changed.");
    }
    public String getCurrentState(){
        return this.currentState;

    }
    public enum States{
        STATE1, STATE2, STATE3;

    }

}

Controller.java

package test;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/*
 * This class would be used for things such as the menu bar etc.
 */

public class Controller implements ActionListener{

    private Model model;
    private View view;

    public Controller(Model model, View view){
        this.model = model;
        this.view = view;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        //...
    }

}

LoginView.java

package test;

import javax.swing.JButton;
import javax.swing.JPanel;

public class LoginView {

    private JPanel panel;
    private JButton loginButton;

    public LoginView(){
        panel = new JPanel();
        loginButton = new JButton("Login");
        panel.add(loginButton);
    }
    public JButton getLoginButton(){
        return this.loginButton;
    }
    public JPanel getPanel(){
        return this.panel;
    }
}

LoginModel.java

package test;

public class LoginModel {

    private String username, password;

    public LoginModel(){

    }

    public boolean validateCredentials(){
        //Access database and validate credentials
        return true;
    }

}

LoginController.java

    package test;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class LoginController implements ActionListener{

    private LoginView view;
    private LoginModel loginModel;
    private Model stateModel;

    public LoginController(Model stateModel, LoginModel loginModel, LoginView view){
        this.view = view;
        this.loginModel = loginModel;
        this.stateModel = stateModel;

        view.getLoginButton().addActionListener(this);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == view.getLoginButton()){
            if(loginModel.validateCredentials()){
                //A change of state would show a different card in the GUI view.
                stateModel.changeState(Model.States.STATE2);

            }else{
                //Don't change the GUI's state.
            }
        }
    }
}
Community
  • 1
  • 1
Astronought
  • 405
  • 5
  • 24
  • 1
    Please edit your question to cite the other "question on here" and include a [mcve] that shows your current approach; more on this topic maybe found [here](http://stackoverflow.com/a/25556585/230513). – trashgod Jul 23 '16 at 02:45
  • Apologies for not linking the question, I have now edited my original post. I currently don't have a working example I can provide unfortunately as I'm planning the approach I'm going to take before I begin coding. Really what my question boils down to is, is it acceptable for a controller to reference more than one model (in my case the login model and the system state model). – Astronought Jul 23 '16 at 18:43
  • I typically use a modal dialog for login, either per session or per connection. I can't offer a general answer to _is it acceptable for a controller to reference more than one model?_ – trashgod Jul 23 '16 at 22:16
  • I've edited my original post and included a basic code example. I haven't included the observer in the model class that handles the states and a string is simply printed to the console when a change in state occurs. The part I would like to draw your attention to is the LoginController, as you can see I've referenced two models, one being the model which handles the user's credentials and another which controls the state of the application. The whole point of it is so that the main GUI view which includes the JFrame can be notified when a login was successful and alter the card layout. – Astronought Jul 24 '16 at 02:56
  • Would it be correct to infer that your application can be used in a meaningful way when _not_ logged in? If so, I don't see a problem; if not, a modal dialog seems easier. – trashgod Jul 24 '16 at 03:18
  • No, the application wouldn't be able to be used in any meaningful way until the user is actually logged in. A modal dialog does seem more appropriate in this situation and I will consider that approach. However with your knowledge of MVC does the method I've used in my example seem OK regardless? I.E Controllers interacting with multiple models. It's likely I will have several other views for the application and I'm really trying to avoid putting all the GUI code into one class. – Astronought Jul 25 '16 at 20:41
  • I can't recall seeing that pattern used; instead of multiple models, I typically use aggregation, composition and/or strategy. – trashgod Jul 26 '16 at 03:15
  • I came up with a slightly different solution, instead of having the other controllers reference the state model I instead made the GUI view listen to updates from the login model to determine whether or not the login attempt was successful. – Astronought Jul 26 '16 at 21:08
  • That seems to comport with the outline examined [here](http://stackoverflow.com/a/2687871/230513). – trashgod Jul 26 '16 at 22:14

0 Answers0