Features of JavaFX such as declarative UI scripting, and binding, make it very natural to use a model-view-controller pattern. I typically have at least one class that comprises the model, and usually put model classes in a package whose ending node is "model".
Often it is necessary to share a model instance with more than one view class. In these cases I leverage the Singleton pattern with a factory function to ensure that there is at most one instance of a model class. The screenshot below is the UI of a very simple program that I developed to demonstrate this technique. The screenshot on the right is of the quintessential model Betty Boop :-)
Here's the code behind the example, beginning with the model class:
/*
* SingletonExampleModel.fx
*
* Example of using the Singleton pattern to have share a model class
* by more than one view class in JavaFX.
*
* Developed 2009 by James L. Weaver as a JavaFX example to be freely used
*/
package com.javafxpert.model;
/**
* Singleton instance of model
*/
var modelInstance:SingletonExampleModel;
/**
* Get or create the singleton instance of model
*/
public function getInstance():SingletonExampleModel {
if (modelInstance == null) {
modelInstance = SingletonExampleModel {};
}
return modelInstance;
}
public class SingletonExampleModel {
/**
* String that comprises this very simple model
*/
public var myString:String = "Change me!";
}
The model class above uses a module-level variable to store the singleton instance, and a module-level function that retrieves it (creating one if necessary). This model instance is accessed by the simple, contrived, view classes (CustomNodeA and CustomNodeB) shown in the listings below:
CustomNodeA.fx:
package com.javafxpert.ui;
import javafx.scene.*;
import javafx.scene.control.TextBox;
import com.javafxpert.model.SingletonExampleModel;
public class CustomNodeA extends CustomNode {
/**
* Reference to the model class
*/
var model:SingletonExampleModel = SingletonExampleModel.getInstance();
/**
* Define the scene graph for this custom node
*/
public override function create() {
var tbRef:TextBox = TextBox {
text: "Change me!"
columns: 15
action: function():Void {
model.myString = tbRef.text;
}
}
}
}
CustomNodeB.fx:
package com.javafxpert.ui;
import javafx.scene.*;
import javafx.scene.text.*;
import com.javafxpert.model.SingletonExampleModel;
public class CustomNodeB extends CustomNode {
/**
* Reference to the model class
*/
var model:SingletonExampleModel = SingletonExampleModel.getInstance();
/**
* Define the scene graph for this custom node
*/
public override function create() {
Text {
content: bind model.myString
font: Font.font("default", FontWeight.BOLD, 24)
}
}
}
Finally, here is the main script that puts the custom nodes shown above in a scene on the stage:
SingletonExampleMain.fx
package com.javafxpert.ui;
import javafx.stage.Stage;
import javafx.scene.*;
import javafx.scene.layout.VBox;
Stage {
title: "Singleton Model Example"
scene: Scene {
width: 300
height: 200
content: VBox {
translateX: 50
translateY: 50
spacing: 30
content: [
CustomNodeA {},
CustomNodeB {}
]
}
}
}
Using this technique, any number of view classes can access a single, shared, model instance.
Regards, and please leave a comment if you have any questions,
Jim Weaver
JavaFXpert.com
Hi Jim,
I am a big fan of design patterns and transmit my enthusiasm to my students.
MVC is a wonderful architecture but I usually warn my students about the singleton pattern.
I would recommend passing the model as a parameter to each view instead of using a singleton pattern.
Singleton is good for the problem it solves (implementing global variables) but is often overused as it is easy to explain and understand.
Singleton causes many unnecessary dependencies and maintenance nightmares.
In your example, when you will want to have two models in your application, your views won't be able to cope with this new setup.
Interested readers can do a google search on evil singleton:
http://www.google.com/search?hl=en&q=evil+singleton
Thanks for your blog,
Posted by: Rémi | April 06, 2009 at 04:46 PM