In short: Views send UI Events to Controllers, Controllers figure out from Events what to send to Models, and Models, once processed data from Controllers, let Views know data has changed.
The short story:
Views are UIs, Controllers are the ActionListeners, and Models are everything else. One major advantage is that you can then take the same model, give it to 2 different Views, and if one View changes, so can the other, reflecting the same change, or you can change how your View looks entirely without changing the underlying Model, or have the Model change and not have to change the UI. Controllers translate when the UI wants to do something into something that the Model can do with it, so that the View can update it's display based on what the Model has.
The long of it:
At least in my experience, the MVC style attempts to separate responding to UI specific events from the displaying of UI specific components with Controllers responding to UI specific events, but UI displays handled by the View, and the Controllers responding to UI events by essentially tossing them straight at the model, and not themselves returning anything.
To pull an example from your Menu.java
class , for example, this:
this.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
Menu.this.Buscar.setFocusable(false);
}
});
Is a Controller - or rather, an in-line Controller (You can sometimes just have the method attached to the UI class, and just go this.addMouseListener(this);
- sometimes called a "Delegate" or a "View-Controller" - when stuff like the above feels like one line that you really don't want to make a whole dedicated class for the Controller. It can work, but it can sometimes not be ideal, like here.). Everything after this.addMouseListener
could be it's own dedicated class, that you then construct in here, and have it respond to the events.
I'm going to build off of it a little bit to show where this may be useful - I'm using Google Translate to identify that as a thing that indicates that you're searching for something in, for example, the stuff that you're storing in a .csv - we're going to make a Controller for this so that, if hypothetically you were to provide it different Views, you could still use the Controller for a different View.
So a Controller class would look like this:
import javax.swing.*;
import java.awt.event.MouseAdapter;
public class AreaController extends MouseAdapter
{
private JTextArea thingToPullFrom;
public AreaController(JTextArea buscar){
this.thingToPullFrom = buscar;
}
public void mouseClicked(){
this.thingToPullFrom.setFocusable(false);
}
}
then Menu.java
would do this:
this.addMouseListener(new AreaController(this.Buscar));
That's great, but it isn't doing anything with the Buscar
information, and let's say that we want to use that to, as part of the View, display if we've found a Usario
in the Global.java
class. Instead of having everything know how to pull Usario
s out, we might do this with a non-static Global.java
class.
In Progama.java
:
public final class Programa {
public static void main(String[] args) {
Global globalModel = new Global();
try {
Menu menu = new Menu(gloablModel);
} catch (Exception e) {
System.out.println("Erro! O usuário não existe!");
System.exit(1);
}
The in Menu.java
, we'd do this:
public class Menu extends JPanel {
private Global globalModel;
public Menu(boolean darkTheme, Global globalModel) {
this.globalModel = globalModel;
this.darkTheme = darkTheme;
this.init();
}
public void init(){
// Other init function stuff here
this.addMouseListener(new AreaController(this.Buscar, globalModel));
}
}
That's a little bit of refactoring here, and probably not the intended output we really want here, and you may just want to create the model another way, but then you can do this in the AreaController class:
import javax.swing.*;
import java.awt.event.MouseAdapter;
public class AreaController extends MouseAdapter
{
private JTextArea thingToPullFrom;
private Global globalModel;
public AreaController(JTextArea buscar, Global globalModel){
this.thingToPullFrom = buscar;
this.globalModel = globalModel;
}
public void mouseClicked(){
this.thingToPullFrom.setFocusable(false);
globalModel.usarioExiste(this.thingToPullFrom.getText());
}
}
And this way, the Global.java
class would take the method from Usario.java
, and instead of needing to pass in a List<Usario>
every time, the Global.java
could, hypothetically hold the list already.
Except that's...not going to update the UI at all - the usarioExite()
method in Usario.java
currently returns a boolean, and it's being essentially ignored by the Controller. But, in the MVC approach, that is the intended way of avoiding passing information back up the Controller - the Controller should not care what the Model is doing to get the thing it wants, especially if it's going to be multi-threaded, etc. It just wants to tell the model to take the data it got from the View (In this case, the JTextArea Buscar
, and get the model to do something.
So instead, we're going to instead of using that, have a variable in Global.java
that refers to a specific seleccionadoUsuario
In Global.java
:
public final class Global {
private static List<Usuario> usuarios;
private static Usario seleccionadoUsuario;
private Global() {
}
public static List<Usuario> getUsuarios() {
if (Global.usuarios == null) {
Global.usuarios = carregarUsuarios();
}
return Global.usuarios;
}
public void setSeleccionadoUsario(String nombreDeUsario){
if(usarioExiste(nombreDeUsario)){
seleccionadoUsario = null;// something here instead of null, to actually get the user.
}else{
selecciandoUsario = null;
}
}
public getSeleccionadoUsario(){
return seleciandoUsario;
}
}
I'm going to leave the actual selecting of the usario/user to be a mystery in case this is meant to be the actual homework, and I don't know your requirements, but...
What that means is that, if you want to do something with the seleciandoUsario
in the view, you would go globalModel.getSeleciandoUsario()
, and then use that value in displaying it.
That still technically doesn't solve the fact that the Model is not sending information to the View, though there are at least a couple ways of doing that:
1.) Continuously polling on globalModel.getSeleciandoUsario()
to see if it's not null, then add it to whatever you wanted to do;
2.) Using the Observer pattern via the Java Observer and Observable interfaces, and then using setChanged()
in the model to indicate that any View attached as an Observer knows that something has changed.
In this way, your Global.java
Model is doing a larger amount of the data, and probably interacting with the rest of your modelo
classes, and your Controller only essentially says "You know how to do this method - do this thing, with the thing I've received", and your View only cares about "Do I need to present something new to the user? If not, I'm just waiting, and if something comes up, I know which Controller to bother."
EDIT: So, for example Progama.java
could do this, hypothetically:
public final class Programa {
public static void main(String[] args) {
Global globalModel = new Global();
try {
Menu menu = new Menu(globalModel);
Menu menu2 = new Menu(globalModel); // Anything you do in menu will affect data that menu2 uses, and vice versa. Well, once the Model -> View connection is done as discussed above.
} catch (Exception e) {
System.out.println("Erro! O usuário não existe!");
System.exit(1);
}