Code review: Simple MVC

4

Hello world!

I tried to make a very simple MVC in JavaFX. My model is a Pessoa class that has nome and idade (the idade field in case it does not get used). Two text fields represent two views. For simplicity both show the same thing: the name of the person. If you edit one of the names and der Enter , the change is reflected in the template and per table in both text fields again.

Questions:

  • Is this code in MVC?

  • Who is the controller?

  • Should not there be a class for the model named "Template"?

  • Should not there be a class for the controller called "Controller"?

  • What other criticisms can be made of this MVC attempt?

  • App.java

    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;
        }
    }
    

    Person.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();
        }
    }
    

    Observer.java

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

    EDIT:

    Looking at the answer and comparing an example of MVC with FXML I found another example without FXML that came in handy. I have based on it, however without using start() objects of JavaFX, and I now have the following code:

    App.java

    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);
        }
    }
    

    Controller.java

    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;
        }
    }
    
    Is it better? Now Vision and Controller are classes and the *Property method has been simplified to just link them to each other and to the Model.

    By this code I conclude that it will cost a bit by default 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, I pass through the Controller and I return the coding of View. It is rather non-linear development to follow this line.

    I also noticed that the start() class was completely expendable, are you sure?

    In time, should the model be observable as a rule?

        
    asked by anonymous 05.06.2018 / 16:21

    1 answer

    3

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

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

    Imagetakenfrom:www.devmedia.com.br/guia/asp-net-mvc/38190

    Aspreviouslystated,themodelsmustbetheentitiesoftheapplicationdomain,thecontrollersareresponsibleforreceivingthe"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. You can even say that the class App is the controller because it calls the view at the end (I'm assuming stage.show() does this).

      

    Should not there be a class for the model named "Model"?

    No. The model is correct, the Pessoa class is a template, as well as all application domain classes will be the template. For example, if you are going to improve the application and decide that it is also possible to register cars, the Carro class will also be a template, and so on.

      

    Should not there be a class for the controller called "Controller"?

    Yes. I mean, the name of the class who chooses is you, this is going to be indifferent, but there should be a class to play the role of controller.

      

    Should not the start() method be as simple as possible, consisting only of binding the involved parts (Model, Views and Controller)?

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

      

    What other criticisms can be made of this MVC attempt?

    In addition to what has been 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 view. That is, a view is a screen or part of one.

        
    05.06.2018 / 18:17