JavaFX Series - Separation & Life Cycle


Posted by Steven

With the last post of this series, I started experimenting with JavaFX. Since then, I figured out how to separate the user interface (UI) framework from the business logic and how the life cycle of the objects should be like.

First impressions

As I explained before, I want to write a small simulation in which some civilization uses growing resources to expand. The whole idea is pretty scetchy right now, but there should be a bord on which the whole simulation takes place and the mentioned resources. Because resources don't grow on their own, there are resource spawners that can be set programmatically and appear on the UI. These things spawn new resources every time the user clicks on them. The new resources (yellow thingies) are spread in a random fashion around the spawners (grey buttons):

Package Hierarchy & Life Cycle

My refined package hierarchy looks as follows. The numbers in the diagram show in which order the packages are accessed.

  1. General application setup. Currently there is just one class that decides what UI is used.Every setup for other UI frameworks has to go here.
  2. UI-specific classes. The class JavaFXApplication in the package ui.javaFX is a subclass of javafx.application.Application and thus starts the FX frontend.
  3. After starting the frontend, the business logic in form of the class World is instantiated. Every business decision, such as creating the world with two resource spawners, is made in the business package. That way, the business logic is isolated from the UI. To get notified of changes in the business layer (for example new grown resources from the spawners), the JavaFXApplication offers the method notifyCreation(WorldObject newWorldObject). The class ResourceSpawner in the business package creates the new resource and cares about everthing business-specific. After that, it notifies the UI that it can display the new object. How a business object can be displayed in a UI is described in the next section.

business objects and graphical representations

Because my business layer has to be independent from the UI layer, business objects that are visible to the user must not hold any specifics of their appearance. The business class Resource for example only knows where it is positioned in the world, but not how it will look like. That is the responsability of the UI:

  1. public class Resource implements WorldObject {
  2.  
  3. // (only holds x and y coordinates)
  4. private CartesianCoordinate position;
  5.  
  6. public Resource(CartesianCoordinate position) {
  7. super();
  8. this.position = position;
  9. }
  10.  
  11. public CartesianCoordinate getPosition() {
  12. return position;
  13. }
  14. }

As you can see above in the class diagram, there is a package worldObjects in the business- and in the ui-package. For every business class that is visible on the UI (namely worldObjects), there is a graphical representation class. This class encapsulates the represented business class and cares about the graphical representation. Hence, these classes are UI framework specific. That is the reason why ResourceFX, the graphical representation of the business class Resource for the framework JavaFX, is a subclass of Polygon which is a JavaFX class:

  1. public class ResourceFX extends Polygon {
  2.  
  3. private Resource representedResource;
  4.  
  5. public ResourceFX(final Resource representedResource) {
  6. super(new double[] { 0, 0, 10, 10, 20, 0, 20, 10, 30, 20, 20, 20, 20,
  7. 30, 10, 30, 0, 20, 10, 20 });
  8. this.setFill(Color.CORAL);
  9. this.representedResource = representedResource;
  10. setLayoutX(representedResource.getPosition().getX());
  11. setLayoutY(representedResource.getPosition().getY());
  12. }
  13. }

As I described, the business logic is started after the UI has been initialized. At first, I tried to implement it the other way around. First the World-class which holds all other business classes was instantiated, then the two resource spawners where added. Only after that the JavaFX UI was started. That worked fine for this static setup. But because a JavaFX application never leaves the launch() method except when being closed, there was no way to add more world objects after starting the UI. This lead to the new hierarchy as described above.

Get the code

I pushed the code in my repository on github. The codebase for this article is tagged as Codebase_SeparationAndLifeCycle.

Next steps

The next step will be to add some more logic, maybe visually growing resources that disappear after a while. Maybe I will jump ahead and style the growing resources with the powers of CSS.

What do you think - will my concept of separating the business layer from the UI hold? What should I implement next?

Category: 

Comments

I recently developed a JavaFX application (a simple game) and structured it similarly.  Game objects had a class to represent the UI independent state of the domain object (e.g. Insect) then a corresponding Skin object which represented the UI of the object (e.g. InsectSkin). 

The structure was very flexible as it allowed me to change the skin implementations drastically without changing the domain objects or their interfaces - making any changes to look and feel very localized.  Additionally, for some of the skins, I defined their components and layouts in fxml and styled them via css.  This allowed me to use SceneBuilder to modify the layout visually and make declarative changes to the styling of the objects with minimal back end code updates - most changes could be seen in real-time in the SceneBuilder preview screen.

I did find that, if the skin implemented an animation, sometimes I needed to provide feedback to the corresponding domain model that the animation completed, so that it could flip to it's next state - so sometimes when the skin was created I needed to provide a back reference from the skin to the domain object so that such notification could occur simply.  Similarly sometimes the skin had to update it's view based on an internal change to the domain object - to do this I monitored domain object properties for changes and used the ChangeListeners or binding to trigger corresponding updates in the skin.

In some cases for composite control - an additional mediator was required (in my case a GameController), which was aware of multiple system components (e.g. game logic, menu screens, etc) and was able to co-ordinate between them (for example launch a new game when chosen from a menu, monitor the game state and, on game completion, display a new game menu).  For more complex applications I could easily envision multiple mediators of different types being used.  The JavaFX application class doesn't really fit that bill as it is a single instance targeted at launching and intiailizing the application rather than maintaining mediated control logic, therefore the application class just becomes quite small. 

When I was separating all of the logic like this I was wondering, for such a simple game, was the separation of concerns worth it?  After implementing it and playing with it for a while, the conculsion I came to was - yes, it's absolutely worth it.  This surprised me a little bit because I tend to try to do stuff with minimal frameworks, patterns and infrastructure. 

I have created other JavaFX projects which did not have this seperation and found, though they were quite a bit quicker to create, they were harder to maintain than the one with greater seperation of concerns.  So there is value in design and architecture - just don't let it get in the way of getting actual stuff done - it's all about tradeoffs :-)

Sometimes working out how to split your app up into components is the easy part and the more difficult part is to get all of the components to communicate well together.  For larger applications, I think an event bus mechanism would come in handy.  Especially when fxml controllers become involved, it can become a confusing working out how to hook all of the disparate pieces together with dynamic notification.  Generating events, sticking them on the bus and having registered listeners pull them off the bus when required can serve to decouple components further.  Although you could build a simple bus pretty easily, JavaFX doesn't currently have such an event bus architecture built in.  Enhanced functional support via lambda for a Reactive Programming framework such as .net's Reactive Extensions would also be pretty nice.

If you want further justification for such a design decision, the JavaFX control library is structured the same way, with controls encapsulating the public API interface, behaviours mapping specific behavioural logic such as defining and responding to key accelerators and skins defining the look.