0

I am using an MVC architecture.

The SignupController creates a User object from the SignupView.

Where should this User be stored?

I have tried storing it into the MainController, but when I need to access the User object I need to initialise the MainController again, so the User is now null.

Thanks!

  • If you intended to share the `User` object among other views, it's most likely best stored in the model layer (either directly or indirectly) - Think about it like this, `User` holds data - where does data belong – MadProgrammer Mar 11 '18 at 23:14
  • That makes sense. But how would I go about storing it in the Model layer? If I create a User object from the User class, where does it go? I hope I'm making sense! – CodingHedgehog Mar 11 '18 at 23:19
  • **Model** stores the abstract state of your program. **View** is how you display what is written in your model. **Controller** adds the logic by determining how the data is transformed when certain events happen. Imagine a card game, **Model** stores all cards, the players, points, card game rules and so on. The **View** could be a GUI window displaying cards and players. The **Controller** defines what happens when someone in the view clicks on the deck to draw a card, it then adds a card to the hand of a player in the model. – Zabuzard Mar 11 '18 at 23:29
  • I understand that. I don't think I'm getting my point across clearly. If I create a user like this: `User user = new User(name);` Then where can I store the reference to this object where every controller can access it? – CodingHedgehog Mar 11 '18 at 23:37
  • @CodingHedgehog Perhaps, instead of the view creating the `User` object, have the view pass the information to the controller and have the controller ask the model to create the `User` – MadProgrammer Mar 11 '18 at 23:45
  • @MadProgrammer I did this initially, but now the reference to the object is stored in the controller, and I cannot access it from other controllers – CodingHedgehog Mar 11 '18 at 23:55
  • @CodingHedgehog The model should be storing, it's just the controller's job to request that the model perform the operation – MadProgrammer Mar 11 '18 at 23:56
  • @MadProgrammer Could you give me an example? I am confused how if I create a User object I can allow the reference to that very object available for every controller. Is it a separate class inside the model that stores the reference? – CodingHedgehog Mar 11 '18 at 23:59

2 Answers2

2

Okay, let's take a step back. User is data, in this context, data should be managed and maintained by the model layer.

The controller is responsible for coordinating actions and notifications between the view and the model. In this sense, when the user triggers a request in the view (ie to perform a sign up operation), the view triggers an event, which the controller subscribes to. The controller obtains the required information from the view and passes that information to the model. This might then work backwards, the model triggers an event based on the success of the task and the controller would notify the view ... as required

The important part of all this, is no one part knows how the other part actually works ... and doesn't care

Let's start with the basics. You need some way to represent a "user"

public interface User {
    public String getName();
}

Now, you need someway to create a User...

public interface UserFactoryListener {
    public void userCreated(User user);
    public void userCreationFailed(Exception exp); // Or some other error object
}

public interface UserFactory {
    public void makeUser(String name);

    public void addUserFactoryListener(UserFactoryListener listener);
    public void removeUserFactoryListener(UserFactoryListener listener);
}

So, this is a basic concept of a factory which will take the name of the user and create a new User object, the important thing to note here, is we don't care "how" that is done, only that is done and that it will generate either a success or failure event

But, what if we also want the ability to authenticate a user?

public interface UserAuthenticationistener {
    public void userAuthenticated(User user);
    public void userAuthenticationFailed(Exception exp); // Or some other error object
}

public interface UserAuthenticator {
    public void authenticateUser(String name, char[] password) throws SecurityException;

    public void addUserAuthenticator(UserAuthenticator listener);
    public void removeUserAuthenticator(UserAuthenticator listener);
}

So, this is the same basic idea, it takes a name and password and authenticates the user in someway, which we don't care about, and will generate a success or failure event which we can subscribe to.

"Yes, but what about the user model?" I hear you say, glad you asked!

public interface UserSigninModel extends UserFactory, UserAuthenticator {
    
}

Well, okay, this is the sign in model, but it incapsulates the two primary functions we want to support, sign up or login.

Now, we need some way to coordinate the two, this is the job of the controller...

public interface UserSigninController extends UserSigninViewListener, UserFactoryListener, UserAuthenticationistener {
    public UserSigninModel getModel();
    public UserSigninView getView();
}

... well, that was kind of underwhelming, but that's kind of the point

Finally, we need some way to interact with the actual user, the view!

public interface UserSigninViewListener {
    public void signupUser(UserSigninView view);
    public void authenticateUser(UserSigninView view);
}

public interface UserSigninView {
    public Pane getView();
    
    public String getName();
    public char[] getPassword();
    
    public void addUserSigninViewListener(UserSigninViewListener listener);
    public void removeUserSigninViewListener(UserSigninViewListener listener);
    
    public void userSignupFailed(Exception exp);
    public void userAuthenticationFailed(Exception exp);
}

So, all of this does one simple job. It describes the intent (or the contracts) that each layer is expected to provide. At no point does any one part actually care how the other part is implemented, only that the contract is upheld

For example, a possible controller implementation might look something like...

public class DefaultUserSigninController implements UserSigninController {
    private UserSigninModel model;
    private UserSigninView view;

    public DefaultUserSigninController(UserSigninModel model, UserSigninView view) {
        this.model = model;
        this.view = view;
    }

    @Override
    public UserSigninModel getModel() {
        return model;
    }

    @Override
    public UserSigninView getView() {
        return view;
    }

    @Override
    public void signupUser(UserSigninView view) {
        getModel().makeUser(view.getName());
    }

    @Override
    public void authenticateUser(UserSigninView view) {
        getModel().authenticateUser(view.getName(), view.getPassword());
    }

    @Override
    public void userCreated(User user) {
        // Coordinate with the navigation controller to move to the
        // next part of the program. This might pass the User object
        // to the navigation controller (as a event) so it
        // can be seeded into the next model
    }

    @Override
    public void userCreationFailed(Exception exp) {
        getView().userSignupFailed(exp);
    }

    @Override
    public void userAuthenticated(User user) {
        // Coordinate with the navigation controller to move to the
        // next part of the program. This might pass the User object
        // to the navigation controller (as a event) so it
        // can be seeded into the next model
    }

    @Override
    public void userAuthenticationFailed(Exception exp) {
        getView().userAuthenticationFailed(exp);
    }
    
}

The controller itself isn't doing a lot, other then coordinate the communications between the view and the model. It also provides the possibility to provide notifications to another "navigation controller" which might be responsible for determining which controller/view should be shown next. It might even create the "base" model wrapping the new User object in it.

Disclaimer

This is an overly simplified example, intended to provide a demonstrable concept of the work flow - the point been, the "how" is unimportant, as it should all be hidden behind the interface, what's important is the contract that each layer agrees to implement.

Also, my JavaFX is minimal, so the event management could be changed to better suit how the API actually implements it's observable pattern

Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
0

As far as I understand based on the fxml tag, you are building the application with Java FX, meaning that there will be only one User at any one time in the application. In this case, the best way would be to store it inside a separate class, like a service or a repository.

Example:

public class UserService {

    protected User user;

    public Optional<User> getUser() {
        return Optional.ofNullable(user);
    }

    public void setUser(User user) {
        this.user = user;
    }

}

Then either use Dependency Injection (e.g. Guice) or some sort of a Singleton Pattern implementation, e.g. getInstance().

In case you are talking about a web application, I would suggest to keep the reference in the session, as this would bind the User to the actual user performing HTTP requests.

E. Makarovas
  • 616
  • 7
  • 6
  • Thanks for the advice. How would I do the same thing for multiple objects, e.g. multiple cars. How would I allow every controller to access all the car references I've created? If I create them in a controller then other controllers cannot access them – CodingHedgehog Mar 11 '18 at 23:57
  • It's not the responsibility of the controller to create those objects, it is its responsibility to coordinate between the model(s) and the view(s) that such operations occur as and when required and coordinate the interaction between the view(s) and the model(s) – MadProgrammer Mar 12 '18 at 00:00
  • @MadProgrammer Who is responsible for creating the object? If the controller or view does not do it, then how do they get created? – CodingHedgehog Mar 12 '18 at 00:08
  • @CodingHedgehog In your case, I vote that the model does it – MadProgrammer Mar 12 '18 at 00:23