Code Review: Simple MVC

Asked

Viewed 127 times

5

Hello world!

I tried to make a very simple MVC in Javafx. My template is a class Pessoa who owns nome and idade (the field idade in case it is not used). Two text Fields represent two views. For simplicity both show the same thing: the person’s name. If you edit one of the names and give Enter, the change is reflected in the model and by table in the two text Fields again.

Questions:

  1. This code is in MVC?

  2. Who is the controller?

  3. There shouldn’t be a class for the model called "Model"?

  4. There should not be a class for the controller called "Controller"?

  5. The method start() should not be as simple as possible, consisting only of connecting the involved parties (Model, Views and Controller)?

  6. What other criticisms can be made of this MVC attempt?

Java app.

package piovezan.mvcdesktop;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;

public class App extends Application {

    private final Pessoa pessoa = new Pessoa("Mario", 39);

    public static void main( String[] args ) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {

        atribuirTituloAoPalco("Hello World!", stage);

        TextField visao1 = criarTextField(pessoa.getNome(), e -> pessoa.setNome(((TextField)e.getSource()).getText()));
        TextField visao2 = criarTextField(pessoa.getNome(), e -> pessoa.setNome(((TextField)e.getSource()).getText()));

        pessoa.adicionarObservadorDoNome(nome -> visao1.setText(nome));
        pessoa.adicionarObservadorDoNome(nome -> visao2.setText(nome));

        FlowPane root = new FlowPane(Orientation.VERTICAL);

        root.getChildren().add(visao1);
        root.getChildren().add(visao2);

        stage.setScene(new Scene(root, 300, 250));

        stage.setOnCloseRequest(v -> pessoa.removerObservadores());
        stage.show();
    }

    private void atribuirTituloAoPalco(String titulo, Stage stage) {
        stage.setTitle(titulo);
    }

    private TextField criarTextField(String texto, EventHandler<ActionEvent> eventHandler) {
        TextField textField = new TextField();
        textField.setText(texto);
        textField.setOnAction(eventHandler);
        return textField;
    }
}

Pessoa.java

package piovezan.mvcdesktop;

import java.util.HashSet;
import java.util.Set;

/**
  * É responsabilidade do implementador da classe escolher tipos imutáveis para os campos que serão observados
  * por cada observador (exemplo: String para nome, Integer para idade) caso contrário o observável irá "vazar"
  * estado para o observador.
  */
public class Pessoa {

    private String nome;

    private int idade;

    private final Set<Observador<String>> observadoresDoNome = new HashSet<>();
    private final Set<Observador<Integer>> observadoresDaIdade = new HashSet<>();

    public Pessoa(String nome, int idade) {
        this.nome = nome;
        this.idade = idade;
    }

    public void setNome(String nome) {
        this.nome = nome;
        notificarQueNomeMudou();
    }

    public void setIdade(int idade) {
        this.idade = idade;
        notificarQueIdadeMudou();
    }

    public String getNome() {
        return nome;
    }

    public int getIdade() {
        return idade;
    }

    public void adicionarObservadorDoNome(Observador<String> observador) {
        observadoresDoNome.add(observador);
    }

    public void adicionarObservadorDaIdade(Observador<Integer> observador) {
        observadoresDaIdade.add(observador);
    }

    public void notificarQueNomeMudou() {
        for (Observador<String> observador: observadoresDoNome) {
            observador.notificar(nome);
        }
    }

    public void notificarQueIdadeMudou() {
        for (Observador<Integer> observador: observadoresDaIdade) {
            observador.notificar(idade);
        }
    }

    public void removerObservadores() {
        observadoresDoNome.clear();
        observadoresDaIdade.clear();
    }
}

Java observer.

package piovezan.mvcdesktop;

@FunctionalInterface
public interface Observador<T> {
    void notificar(T observavel);
}

EDIT:

Analyzing the response and comparing an example of MVC with FXML found another example without FXML that came in handy. I based on it, but without using objects *Property of Javafx, and now I have the following code:

Java app.

package piovezan.mvcdesktop;

import javafx.application.Application;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;

public class App extends Application {

    public static void main( String[] args ) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {

        Pessoa pessoa = new Pessoa("Mario", 39);

        Controlador controlador1 = new Controlador(pessoa);
        Visao visao1 = new Visao(pessoa, controlador1);

        Controlador controlador2 = new Controlador(pessoa);
        Visao visao2 = new Visao(pessoa, controlador2);

        FlowPane root = new FlowPane(Orientation.VERTICAL);
        root.getChildren().add(visao1.comoComponente());
        root.getChildren().add(visao2.comoComponente());

        atribuirTituloAoPalco("Hello World!", stage);
        stage.setScene(new Scene(root, 300, 250));

        stage.setOnCloseRequest(v -> pessoa.removerObservadores());
        stage.show();
    }

    private void atribuirTituloAoPalco(String titulo, Stage stage) {
        stage.setTitle(titulo);
    }
}

Java controller.

package piovezan.mvcdesktop;

public class Controlador {

    private final Pessoa pessoa;

    public Controlador(Pessoa pessoa) {
        this.pessoa = pessoa;
    }

    public void atualizar(String nome) {
        pessoa.setNome(nome);
    }
}

Visao.java.

package piovezan.mvcdesktop;

import javafx.scene.control.TextField;
import javafx.scene.layout.FlowPane;

public class Visao {

    private FlowPane visao;
    private TextField campoNome;

    private Pessoa pessoa;
    private Controlador controlador;

    public Visao(Pessoa pessoa, Controlador controlador) {
        this.pessoa = pessoa;
        this.controlador = controlador;

        criarEConfigurarPane();
        criarEDisporControles();
        atualizarControladorAPartirDeListeners();
        observarModeloEAtualizarControles();
    }

    private void criarEConfigurarPane() {
        visao = new FlowPane();
    }

    private void criarEDisporControles() {
        campoNome = new TextField();
        campoNome.setText(pessoa.getNome());
        visao.getChildren().add(campoNome);
    }

    private void atualizarControladorAPartirDeListeners() {
        campoNome.setOnAction(v -> controlador.atualizar(campoNome.getText()));
    }

    private void observarModeloEAtualizarControles() {
        pessoa.adicionarObservadorDoNome(nome -> campoNome.setText(nome));
    }

    public FlowPane comoComponente() {
        return visao;
    }
}

Got better? Now Vision and Controller are classes and the method start() was simplified to only connect them to each other and to the Model.

By this code I conclude that it will cost a little bit to enter the head because I start with the Model that is disconnected from the others, then I go to the Vision class that is the beginning of the flow, step by the Controller and resume the coding of the Vision. It’s half nonlinear development if you follow that line.

I also noticed that the class Controlador was completely expendable, that’s right?

In time, the model should be observable via rule?

  • Looking like this, it’s not MVC. The model seems clear, but the controller and vision are mixed.

  • 1

    It’s more like an MVP than MVC, in my view. In this case, the class App would play the role of presenter and the view seems to be "hidden" because you don’t make use of a separate code to present the data. Even for MVP is not well in agreement because the view and the presenter are mixed.

  • Vixe. I didn’t get the concept then. I think this question will yield a good answer :)

  • I would say to be happy using MVWYW, but in case you really want MVC in things

  • 1

    By the way, is it Java 8? If so, can you make it more elegant using a pair...

  • I believe that MVC is not limited to codes, but rather strategy, to notice the differences in interpretations or strategies that people adopt see an example from Apple https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/Model-View-Controller/Model-View-Controller.html (is reasonably similar to the approach adopted by Qt, of course you can do as you wish)....

  • ...So in a humble opinion it doesn’t do much to create an MVC of its own and yet it becomes something bureaucratic to organize, I mean, it would be more interesting to create a controller (main) that would select "alone" the controller for the desired view, something that would be semi-automatic and organized in some kind of structure with the namespaces maybe, ie your App.java this "manual" and seems to be the only "controller" (theoretically), I think that the way this does not pay to want to use this design, I think I could look for "automate" things and organize them.

  • Yes, it’s Java 8. I edited the question.

  • @Jeffersonquesado "MV Whatever You Want"? I don’t know if you mean to use alternatives to MVC or do what I can do :P I have difficulties with concepts (even the questions of the site do not always help me) and I believe I should practice the basics, and as I know that Javafx works with MVC, let’s practice MVC :)

Show 4 more comments

1 answer

4


The code presented looks more like a model MVP (Model, View, Presenter) than MVC. Briefly, the main difference between the two standards is that in MVC there is a controller that decides which view will be "shown" in response to some action and in MVP there is a presenter which serves to contain the logic of vision and the vision itself "communicates" with the model.

In general (I won’t go into details because you have several questions about this on the site) the MVC can be summarized by this image:

Image taken from: www.devmedia.com.br/guia/Asp-net-mvc/38190

As stated earlier, the models must be the entities of the application domain, the controllers are responsible for receiving the "actions" of the vision and delivering a new vision. The view is who decides how the data will be shown to the user (for example: an HTML code can define a view).


Trying to focus on your questions:

Who is the controller?

Exactly, it does not exist there. One can even say that the class App is the controller because she calls the view at the end (I’m guessing that stage.show() do this).

There shouldn’t be a class for the model called "Model"?

No. The model is correct, the class Pessoa is a model, just like all classes relating to the field of application will be the model. For example, if you improve the application and decide that it is also possible to register cars, the class Carro will also be a model and so on.

There should not be a class for the controller called "Controller"?

Yes. I mean, the name of the class that chooses is you, it will be indifferent, but so there should be a class to play the role of controller.

The method start() should not be as simple as possible, consisting only of connecting the involved parties (Model, Views and Controller)?

I honestly have no idea what the role of the method is start() in the context of the MVC standard. It seems to me more an application entry point method than something related to the standard.

What other criticisms can be made of this MVC attempt?

Beyond what was quoted, it seems to me that you tried to treat each textField as a different view. This approach is not correct, the view is the screen. For example: a set of textField's and other components form a vision. That is, a vision is a screen or part of a.

  • See my question issue.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.