DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Data Engineering
  3. Data
  4. JavaFX: Using Patterns & Clean Code

JavaFX: Using Patterns & Clean Code

Fabrizio Giudici user avatar by
Fabrizio Giudici
·
May. 12, 09 · Interview
Like (0)
Save
Tweet
Share
43.85K Views

Join the DZone community and get the full member experience.

Join For Free
We are seeing quite a number of exciting JavaFX demos around, demonstrating the pretty features of the language and the capability of easily integrating cool graphics. But, as a software designer, I can't prevent myself from seeing that in most examples we see bloated code - no good separation of concerns and poor applications of the MVC pattern. This is reasonable, as JavaFX was a new language and people needed first to be taught about the syntax and features; but now that - I presume - many of us have been introduced with the new language, it's high time we started worrying about good practices, as well as writing self-commenting code. Let's remember that good practices and good design are always more important than the language!

So, let's introduce a very simple project - a Contact List whose specifications are:

  1. We have a list of "contacts", where each item is name, last name, email, phone, etc...
  2. The UI shows a list of contacts, that can be filtered by a text field; if you enter a value in it, only the contacts whose name starts with the entered text are shown. Selections are applied as you type.
  3. Selecting an item in the list of contacts, the details are shown in a form, where you can edit them.
  4. At any selection change, the form is animated (a rotation and a blur effect).

You can get the code from:

svn co -r 37 https://kenai.com/svn/javafxstuff~svn/trunk/ContactList/src/ContactList

The model

Let's first have a quick look at the model classes. First a simple value object representing a piece of data:

package it.tidalwave.javafxstuff.contactlist.model;

public class Contact
{
public var id: String;
public var firstName: String;
public var lastName: String;
public var phone: String;
public var email: String;
public var photo: String;

override function toString()
{
return "\{id: {id}, value: {firstName} {lastName}, phone: {phone}, email: {email}"
}
}

Then a small service which provides a bunch of data:

package it.tidalwave.javafxstuff.contactlist.model;

public abstract class ContactRegistry
{
public abstract function items() : Contact[];
}

In the demo code, you'll find a mock implementation with some wired values; in a real case this could be a Business Delegate encapsulating code for retrieving data remotely.

So far, so good - it's pretty normal to keep these things separated from the UI.

The Controllers

We're not going to see a classic Controller here; actually, we're slightly departing from the "pure" MVC. The code I'm showing you is more a "Presentation Model", a pattern described by Martin Fowler as:

The essence of a Presentation Model is of a fully self-contained class that represents all the data and behavior of the UI window, but without any of the controls used to render that UI on the screen. A view then simply projects the state of the presentation model onto the glass.

This class is basically an hybrid between a classic model and a controller. It is also a façade between the view and the domain model.

package it.tidalwave.javafxstuff.contactlist.model;

import it.tidalwave.javafxstuff.contactlist.model.ContactRegistry;
import it.tidalwave.javafxstuff.contactlist.model.ContactRegistryMock;

public class PresentationModel
{
public var searchText : String;

public var selectedIndex : Integer;

// The Business Delegate
def contactRegistry = ContactRegistryMock{} as ContactRegistry;

def allContacts = bind contactRegistry.items();

// The contacts filtered according the contents of the search field
public-read def contacts = bind allContacts[contact | "{contact.firstName} {contact.lastName}".startsWith(searchText)];

// The selected contact; the code also triggers a notification at each change
public-read def selectedContact = bind contacts[selectedIndex] on replace previousContact
{
onSelectedContactChange();
};

// Notifies a change in the current Contact selection
public-init var onSelectedContactChange = function()
{
}
}

Note the extreme compactness brought by functional programming. There's almost no imperative programming as everything is achieved by properly using the binding feature. The only imperative part is the 'onSelectedContactChange' function, which is just a listener to notify selection changes to some external code - it will be used only for triggering the animation.

BTW, I'd like to remove it from here, but I wasn't able to. Maybe it's a JavaFX thing that I've not understood yet, but I'm keeping it for another post.

Now, everything about the animation goes encapsulated in a specific class, which only exposes two properties controlling the animation: the effect and the rotation angle. A single play() function is provided to start the animation.

package it.tidalwave.javafxstuff.contactlist.view;

import javafx.animation.Interpolator;
import javafx.animation.Timeline;
import javafx.scene.effect.GaussianBlur;

public class AnimationController
{
public-read var rotation = 0;
public-read def effect = GaussianBlur{}

def timeline = Timeline
{
repeatCount: 1
keyFrames:
[
at(0s) { effect.radius => 20; rotation => 45 }
at(300ms) { effect.radius => 0 tween Interpolator.EASEBOTH;
rotation => 0 tween Interpolator.EASEBOTH }
]
}

public function play()
{
timeline.playFromStart();
}
}

The Views

Now, a UI component. The way we design it largely depends on the process, as a graphic designer could be involved. In any case, I think that the whole UI should not be implemented in a single, bloated class; rather relevant pieces should be split apart. For instance, a CustomNode can model the "form" that renders the contact details (in the code below I've omitted all the attributes related to rendering):

package it.tidalwave.javafxstuff.contactlist.view;

import javafx.scene.CustomNode;
import javafx.scene.Group;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.Node;
import javafx.ext.swing.SwingLabel;
import javafx.ext.swing.SwingTextField;
import it.tidalwave.javafxstuff.contactlist.model.Contact;

public class ContactView extends CustomNode
{
public var contact : Contact;

public override function create() : Node
{
return Group
{
content:
[
VBox
{
content:
[
SwingLabel
{
text: bind "{contact.firstName} {contact.lastName}"
}
HBox
{
content:
[
SwingLabel { text: "First name: " }
SwingTextField { text: bind contact.firstName }
]
}
HBox
{
content:
[
SwingLabel { text: "Last name: " }
SwingTextField { text: bind contact.lastName }
]
}
HBox
{
content:
[
SwingLabel { text: "Email: " }
SwingTextField { text: bind contact.email }
]
}
HBox
{
content:
[
SwingLabel { text: "Phone: " }
SwingTextField { text: bind contact.phone }
]
}
]
}
]
};
}
}

As you can see, we have only layout and data binding here, the only things a view should do.

 

Putting all together

Now the last piece of code, the Main, which builds up the application (again, I've omitted all attributes only related to rendering):

 

 

 

 

package it.tidalwave.javafxstuff.contactlist.view;

import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.ext.swing.SwingLabel;
import javafx.ext.swing.SwingList;
import javafx.ext.swing.SwingListItem;
import javafx.ext.swing.SwingTextField;
import it.tidalwave.javafxstuff.contactlist.controller.PresentationModel;

Stage
{
def animationController = AnimationController{};

def presentationModel = PresentationModel
{
onSelectedContactChange : function()
{
animationController.play();
}
};

scene: Scene
{
content: VBox
{
content:
[
HBox
{
content: SwingLabel { text: "Contact List" }
}
HBox
{
content:
[
SwingLabel { text: "Search: " }
SwingTextField { text: bind presentationModel.searchText with inverse }
]
}
HBox
{
content:
[
SwingList
{
items: bind for (contact in presentationModel.contacts)
{
SwingListItem { text:"{contact.firstName} {contact.lastName}" }
}
selectedIndex: bind presentationModel.selectedIndex with inverse
}
ContactView
{
contact: bind presentationModel.selectedContact
effect: bind animationController.effect
rotate: bind animationController.rotation
}
]
}
]
}
}
}

As in the previous code snippets, I think the listing can be easily read and understood. Basically, we are glueing all the pieces together and binding the relevant models. 

In the end, each class in this small project does a simple, cohese thing: representing data, encapsulating the presentation logic, controlling the animation, rendering the views. Dependencies are reduced to the minimum and they have the correct direction: views depend on the models (and not the opposite) and the AnimationController; the AnimationController is independent. You could replace the view components without affecting the rest of the classes, as well as removing or adding other animations by properly using different AnimationControllers. This good separation of roles and responsibilities is the good way to apply OO.

There is a detail which is worth discussing. Note the two bind ... with inverse. They implement the so-called "bidirectional binding" where not only a change in the model (e.g. PresentationModel.selectedIndex) is reflected to attributes in the UI (e.g. SwingList.selectedIndex), but also the opposite happens. Indeed, the reverse binding is more important in our example, because it implements the controller responsibility (it captures user's gestures from the view and changes the model); the direct binding from the PresentationModel to SwingList, instead, is  useless, as in our case the PresentationModel is never the originator of a change.

So, why not using a simple, direct binding in PresentationModel towards SwingList? Such as:

public class PresentationModel
{
var list : SwingList;

public def selectedIndex = bind list.selectedIndex;

...
}

Because this would introduce a dependency from the model/controller to the view, which is plain wrong. Here, bind ... with inverse not only works as a shortcut for writing less code (that is, explicitly declaring a binding and its inverse), but it's also an essential feature for a better design.

As far as I know - but I could be wrong - for example in ActionScript (Adobe Flex language) there's no bidirectional binding (you need to put binding keywords at both ends of the association), thus introducing unneeded or circular dependencies. I believe this is true at least at code level (as far as I understand, there are different ways to do binding in ActionScript).

 

JavaFX Data binding

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Reconciling Java and DevOps with JeKa
  • 10 Most Popular Frameworks for Building RESTful APIs
  • Isolating Noisy Neighbors in Distributed Systems: The Power of Shuffle-Sharding
  • Data Stream Using Apache Kafka and Camel Application

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: