© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
K. Sharan, P. SpäthLearn JavaFX 17https://doi.org/10.1007/978-1-4842-7848-2_11

11. Model-View-Controller Pattern

Kishori Sharan1   and Peter Späth2
(1)
Montgomery, AL, USA
(2)
Leipzig, Sachsen, Germany
 
In this chapter, you will learn:
  • What the model-view-controller pattern is

  • What other variants of the model-view-controller pattern are, such as the model-view-presenter pattern

  • How to develop a JavaFX application using the model-view-presenter pattern

The examples of this chapter lie in the com.jdojo.mvc package. In order for them to work, you must add the corresponding lines to the module-info.java file:
...
opens com.jdojo.mvc to javafx.graphics, javafx.base;
opens com.jdojo.mvc.model to javafx.graphics, javafx.base;
opens com.jdojo.mvc.view to javafx.graphics, javafx.base;
...

What Is the Model-View-Controller Pattern?

JavaFX lets you create applications using GUI components. A GUI application performs three tasks: accepts inputs from the user, processes the input, and displays outputs. A GUI application contains two types of code:
  • Domain code that deals with domain-specific data and business rules

  • Presentation code that deals with manipulating user interface widgets

It is often required that the same data in a specific domain be presented in different forms. For example, you may have a web interface using HTML and a desktop interface using JavaFX to present the same data. For easy maintenance of the application code, it is often necessary to divide the application into two logical modules where one module contains presentation code and another domain code (domain-specific business logic and data). The division is made in such a way that the presentation module can see the domain module, but not vice versa. This type of division supports multiple presentations with the same domain code.

The model-view-controller (MVC) pattern is the oldest and the most popular pattern to model GUI applications to facilitate such a division. The MVC pattern consists of three components: model, view, and controller. Figure 11-1 shows a pictorial view of the MVC components and the interactions among them.
Figure 11-1

 Interaction between participants in the classic MVC pattern

In MVC, the model consists of the domain objects that model the real-world problems. The view and controller consist of the presentation objects that deal with the presentation such as input, output, and user interactions with GUI elements. The controller accepts the inputs from the users and decides what to do with it. That is, the user interacts with the controller directly. The view displays the output on the screen. Each view is associated with a unique controller and vice versa. Each widget on the screen is a view, which has a corresponding controller. Therefore, there are typically multiple view-controller pairs in a GUI screen. The model is not aware of any specific views and controllers. However, views and controllers are model specific. The controller commands the model to modify its state. The views and model always stay in sync. The model notifies views about changes in its state, so views can display the updated data. The model-to-view interaction is facilitated through an observer pattern. Keep in mind that the model is fully unaware of any specific views. The model provides a way for views to subscribe to its state change notifications. Any interested views subscribe to the model to receive state change notifications. The model notifies all views that had subscribed whenever a model’s state changes.

What has been described so far about the MVC pattern is the original concept of MVC that was used in developing user interfaces in the Smalltalk-80 language that was created in 1980. There have been many variants of Smalltalk. The concept in MVC that the presentation and domain logic should be separated in a GUI application still holds true. However, in MVC, dividing the responsibilities between three components had issues. Which component, for example, will have the logic to update the attributes of the view, such as changing the view color or disabling it, that depend on the state of the model? Views can have their own states. A list that displays a list of items has the index of the currently selected item. The selected index is the state of the view, not the model. A model may be associated with several views at one time, and it is not the responsibility of the model to store the state of all views.

The issues of which component in MVC has the responsibility of storing the view logic and state led to another variant of MVC called the Application Model MVC (AM-MVC). In AM-MVC, a new component, called Application Model , is introduced between the model and the view/controller. Its purpose is to contain the presentation logic and the state, thus solving the issue of which component keeps the presentation logic and the state in the original MVC. The model in MVC is decoupled from the view, and this is also true in AM-MVC. Both use the same observer technique to keep the view and the model in sync. In AM-MVC, the Application Model was supposed to keep the view-related logic but was not allowed to access the view directly. This resulted in bulky and ugly code when the Application Model had to update the view attributes. Figure 11-2 shows a pictorial view of the AM-MVC components and the interactions among them.
Figure 11-2

 Interaction among participants in the AM-MVC pattern

Later, modern graphical operating systems like Microsoft Windows and Mac OS offered native widgets, which users can interact with directly. These widgets combined the functions of the view and controller into one. This led to another variant of MVC, called the model-view-presenter (MVP) pattern. Modern widgets also support data binding, which helps keep the view and model in sync with fewer lines of code. Figure 11-3 shows a pictorial view of the MVP components and the interactions among them.
Figure 11-3

 Interactions among participants in the MVP pattern

In MVC, each widget on the screen is a view, and it has its own unique controller. In MVP, the view is composed of several widgets. The view intercepts the inputs from the user and hands over the control to the presenter. Note that the view does not react to the user inputs. It only intercepts them. The view is also responsible for displaying the data from the model.

The presenter is notified by the view about the user inputs. It determines how to react to the user’s input. The presenter is responsible for the presentation logic, manipulating the view, and issuing commands to the model. Once the presenter modifies the model, the view is updated using the observer pattern, as was done in MVC.

The model is responsible for storing domain-specific data and logic. Like MVC, it is independent of any views and presenters. The presenter commands the model to change, and the view updates itself when it receives state-changed notifications from the model.

There are some variants of MVP as well. They vary in the responsibility of the view and the presenter. In one variant, the view is responsible for all view-related logic without the help of the presenter. In another variant, the view is responsible for all the simple logic that can be handled declaratively, except when the logic is complex, which is handled by the presenter. In another variant, the presenter handles all view-related logic and manipulates the view. This variant is called passive view MVP in which the view is unaware of the model. Figure 11-4 shows a pictorial view of the components in the MVP passive view and the interactions among them.
Figure 11-4

 Interactions among the participants in the passive view MVP pattern

The concept of MVC that the presentation logic should be separated from the domain logic has been around for over 30 years, and it is going to stay in one form or another. All variants of MVC have been attempting to achieve the same function of what the classic MVC did, though in different ways. The variants vary from the classic MVC in the responsibilities of their components. When someone talks about MVC in a GUI application design, make sure you understand which variant of MVC is used and which components perform which tasks.

A Model-View-Presenter Example

This section presents a detailed example that uses the MVP pattern.

The Requirements

For the example here, you will develop a GUI application that will let the user enter the details of a person, validate the data, and save it. The form should contain
  • Person ID field: An autogenerated unique noneditable field

  • First name field: An editable text field

  • Last name field: An editable text field

  • Birth date: An editable text field

  • Age category: An autocomputed noneditable field based on the birth date

  • Save button: A button to save the data

  • Close button: A button to close the window

The personal data should be validated against the following rules:
  • The first and last names must be at least one character long.

  • If a birth date is entered, it must not be a future date.

The Design

Three classes will represent the three components of an MVP:
  • Person class

  • PersonView and PersonPresenter classes

The Person class represents the model, the PersonView class the view, and the PersonPresenter class the presenter. As required by the MVP pattern, the Person class will be agnostic about the PersonView and the PersonPresenter classes. The PersonView and the PersonPresenter classes will interact with each other, and they will use the Person class directly.

Let’s divide the classes related to the model and the view logically by placing them in different Java packages. The com.jdojo.mvc.model package will contain model-related classes, and the com.jdojo.mvc.view package will contain the view-related classes. Figure 11-5 shows the finished window.
Figure 11-5

 The initial screenshot of the person management window

The Implementation

The following paragraph describes the implementation of the three layers of the MVP example application.

The Model

Listing 11-1 contains the complete code for the Person class. The Person class contains the code for the domain data and the business rules. In real life, you might want to separate the two into multiple classes. However, for a small application like this, let’s keep them in one class.
// Person.java
// ...find in the book's download area.
Listing 11-1

 The Person Class Used As the Model

The Person class declares an AgeCategory enum to represent different ages:
public enum AgeCategory {BABY, CHILD, TEEN, ADULT, SENIOR, UNKNOWN};

The person ID, first name, last name, and birth date are represented by JavaFX properties. The personId property is declared read-only, and it is autogenerated. Relevant setter and getter methods are provided for these properties.

The isValidBirthDate() and isValidPerson() methods are included to perform domain-specific validations. The getAgeCategory() method belongs to the Person class as it computes the age category of a person based on their birth date. I have made up some date ranges to divide the age of a person into different categories. You may be tempted to add this method to the view. However, you would then need to duplicate the logic inside this method for each view. The method uses the model data and computes a value. It knows nothing about views, so it belongs to the model, not to the view.

The save() method saves the personal data. The save method is trivial; it simply displays a message on the standard output if the personal data are valid. In a real-world application, it would save the data to a database or a file.

The View

The PersonView class shown in Listing 11-2 represents the view in this application. It is mainly responsible for displaying the data in the model.
// PersonView.java
// ...find in the book's download area.
Listing 11-2

 The PersonView Class Used As the View

The PersonView class inherits from the GridPane class. It contains an instance variable for each UI component. Its constructor takes the model (an instance of the Person class) and a date format as arguments. The date format is the format used to display the birth date. Note that the format for the birth date is view specific, and it should be part of the view as such. The model knows nothing about the format in which the birth date is displayed by views.

The initFieldData() method initializes the view with the data. I used JavaFX bindings to bind the data in UI nodes to the model data except for the birth date and age category fields. This method synchronizes the birth date and the age category fields with the model. The layoutForm() method lays out the UI nodes in the grid pane. The bindFieldsToModel() method binds the person ID, first name, and last name TextFields to the corresponding data fields in the model, so they stay in sync. The syncBirthDate() method reads the birth date from the model, formats it, and displays it in the view. The syncAgeCategory() method synchronizes the age category field, which is computed by the model based on the birth date.

Notice that the view, the PersonView class, does not know about the presenter, the PersonPresenter class. So how will the view and the presenter communicate? The role of a presenter is mainly to get the user’s inputs from the view and act upon them. The presenter will have a reference to the view. It will add event listeners to the view, so it is notified when the data in the view change. In the event handlers, the presenter takes control and processes the inputs. If the application requires a reference to the presenter in the view, you can have that as an argument to the constructor of the view class. Alternatively, you can provide a setter method in the view class to set the presenter.

The Presenter

The PersonPresenter class shown in Listing 11-3 represents the presenter in this application. It is mainly responsible for intercepting the new input in the view and processing it. It communicates directly with the model and the view.
// PersonPresenter.java
// ...find in the book's download area.
Listing 11-3

 The PersonPresenter Class Used As the Presenter

The constructor of the PersonPresenter class takes the model and the view as arguments. The attachEvents() method attaches event handlers to the UI components of the view. In this example, you are not interested in intercepting all inputs in the view. But you are interested in the birth date changes and the clicking of the Save and Close buttons. You do not want to detect all edit changes in the birth date field. If you are interested in all changes in the birth date field, you would need to add a change listener for its text property. You want to detect changes only when the user is done entering the birth date. For this reason
  • You attach a focus listener to the scene and detect if the birth date has lost the focus.

  • You attach an action listener to the birth date field, so you intercept the Enter key press while the field has focus.

This validates and refreshes the birth date and age category whenever the birth date field loses focus or the Enter key is pressed while focus is still in the field.

The handleBirthDateChange() method handles a change in the birth date field. It validates the birth date format before updating the model. It displays an error message to the user if the birth date is not valid. Finally, it tells the view to update the birth date and age category.

The saveData() method is called when the user clicks the Save button, and it commands the model to save the data. The showError() method does not belong to the presenter. Here, you added it instead of creating a new view class. It is used to display an error message.

Putting Them Together

Let’s put the model, view, and presenter together to use them in an application. The program in Listing 11-4 creates the model, view, and presenter, glues them together, and displays the view in a window as shown in Figure 11-5. Notice that the view must be attached to a scene before the presenter is created. It is required because the presenter attaches a focus change listener to the scene. Creating the presenter before adding the view to the scene will result in a NullPointerException .
// PersonApp.java
// ...find in the book's download area.
Listing 11-4

 The PersonApp Class Uses the Model, View, and Presenter to Create a GUI Application

Summary

It is often required that the same domain data be presented in different forms. For example, you may have a web interface using HTML and a desktop interface using JavaFX to present the same data. For easy maintenance of the application code, it is often necessary to divide the application into two logical modules where one module contains presentation code and another domain code (domain-specific business logic and data). The division is made in such a way that the presentation module can see the domain module, but not vice versa. This type of division supports multiple presentations with the same domain code. The MVC pattern is the oldest and the most popular pattern to model GUI applications to facilitate such a division. The MVC pattern consists of three components: model, view, and controller.

In MVC, the model consists of the domain objects that model the real-world problems. The view and controller consist of the presentation objects that deal with the presentation such as input, output, and user interactions with GUI elements. The controller accepts the inputs from the users and decides what to do with them. That is, the user interacts with the controller directly. The view displays the output on the screen. Each view is associated with a unique controller and vice versa. Each widget on the screen is a view, which has a corresponding controller. In MVC, dividing the responsibilities between three components created issues. Which component, for example, would have the logic to update the attributes of the view, such as changing the view color or disabling it, that depend on the state of the model?

The issues of which component in MVC has the responsibility of storing the view logic and the state led to another variant of MVC called the Application Model MVC. In AM-MVC, a new component, called the Application Model, was introduced between the model and the view/controller. Its purpose is to contain the presentation logic and the state, thus solving the issue of which component keeps the presentation logic and state in the original MVC.

Later, modern graphical operating systems like Microsoft Windows and Mac OS offered native widgets, which users can interact with directly. These widgets combined the functions of the view and controller into one. This led to another variant of MVC, called the model-view-presenter pattern.

In MVC, each widget on the screen is a view, and it has its unique controller. In MVP, the view is composed of several widgets. The view intercepts the inputs from the user and hands over the control to the presenter. Note that the view does not react to the user’s inputs; it only intercepts them. The presenter is notified by the view about the user’s inputs and determines how to react to them. The presenter is responsible for the presentation logic, manipulating the view, and issuing commands to the model. Once the presenter modifies the model, the view is updated using the observer pattern, as was done in MVC.

There are some variants of MVP as well. They vary in the responsibility of the view and the presenter. In one variant, the view is responsible for all view-related logic without the help of the presenter. In another variant, the view is responsible for all the simple logic that can be handled declaratively, except when the logic is complex, which is handled by the presenter. In another variant, the presenter handles all view-related logic and manipulates the view. This variant is called passive view MVP, in which the view is unaware of the model.

The next chapter will introduce you to controls that are used to build the view in JavaFX applications.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.61.226