13 29 Singleton
At this stage our JavaFX to-do list has a working basic interface, but it is not very useful yet: the user can only see a few hard-coded tasks and cannot add their own. They also expect their items to still be there the next time they launch the app. To make that work, we will add a small layer that loads and saves the to-do items in a flat file.
The Application class our Main extends from has a stop() method we can override to save data when the application closes. The controller needs the data to populate the list and also has direct access to its contents when we want to store them. Because both the main class and the controller need to access the same data, we use a singleton: a class that has only one instance during the lifetime of the application, with a private constructor so nothing else can create more instances, and a static method to return that single instance.
Creating the TodoData singleton
Inside the datamodel package we create a new class TodoData. We define a unique instance, a file name where the data will be stored, the in-memory list of items and a date formatter:
public class TodoData {
private static final TodoData instance = new TodoData();
private static final String filename = "TodoListItems.txt";
private List<TodoItem> todoItems;
private DateTimeFormatter formatter;
public static TodoData getInstance() { return instance; }
private TodoData() {
formatter = DateTimeFormatter.ofPattern("MMM d, yyyy");
}
public List<TodoItem> getTodoItems() { return todoItems; }
public void setTodoItems(List<TodoItem> items) { this.todoItems = items; }
}
The constructor is private, which is what makes the class a singleton. The static method getInstance() is the only way for the rest of the app — main class and controller — to reach our data.
Loading items from a file
We add a method that reads the items from disk. We use the older try / finally style here for clarity:
public void loadTodoItems() throws IOException {
todoItems = FXCollections.observableArrayList();
Path path = Paths.get(filename);
// open a BufferedReader and read each line here
}
FXCollections.observableArrayList() gives us a JavaFX-friendly list that the ListView can later react to. We will continue this method in the next video by setting up a BufferedReader and parsing each line into a TodoItem. See you soon.
Summary
This lesson covers the Singleton design pattern in Java, explaining how to create a class with only one instance throughout the entire application lifecycle. The tutorial demonstrates implementing a Singleton by creating a TodoData class with a private constructor, private static instance, and a public getInstance() method, allowing both the Main class and Controller to share a single reference to application data. The pattern is used to manage to-do list items, handle file persistence, and coordinate data access between multiple components in a JavaFX application.
Key points
- Singleton Pattern Definition: Ensures only one instance of a class exists during the entire application execution, providing a controlled way to manage single global resources
- Implementation Structure: Uses a private constructor to prevent external instantiation, a private static instance variable, and a public static getInstance() method that returns the single instance
- Use Case in Application: TodoData singleton provides a shared access point for both the Main class and Controller to manage task data without passing data between components
- Data Persistence: The singleton class stores an observable list of to-do items and handles loading/saving tasks to a flat file, with a filename stored as a static variable
- Date Management: Implements DateTimeFormatter to manipulate and format dates within the application when handling task metadata
FAQ
Why is the Singleton pattern necessary in this todo application?
The Singleton provides a convenient way for the Main class and Controller to access the same instance of TodoData without passing references between them. This ensures a single source of truth for task data and simplifies data coordination across the application.
How does a private constructor prevent multiple instances?
By making the constructor private, external classes cannot instantiate the class using the standard new keyword. Instead, they must use the public static getInstance() method, which always returns the pre-created single instance of the class.
What data and methods does the TodoData singleton contain?
The TodoData singleton stores an observable list of to-do items, a filename variable for file persistence, a DateTimeFormatter for date handling, and methods for loading and saving tasks to a flat file.