I am trying to implement the Observer pattern in a JavaFx application. I've never asked a question here but this is driving me a bit crazy.
Essentially I'm trying to use the Observer pattern to monitor a class that's parsing a file of phone numbers, and update the UI automatically as the file is parsed.
Before I get to my questions, here is my code:
Abstract class Observer.java
public abstract class Observer
{
public PhoneBook numbers;
public abstract void update();
}
I have a class that implements this:
public class PhoneBookObserver extends Observer {
public PhoneBookObserver(PhoneBook numbers)
{
this.numbers = numbers;
this.numbers.attach(this);
}
@Override
public void update()
{
System.out.println(""NUMBER - : " + numbers.GetNumbers());
}
}
In the class doing the parsing, I've created a new PhoneBookObserver
public PhoneBook ParsePhoneBook()
{
PhoneBook nums= new PhoneBook();
PhoneBookObserver p = new PhoneBookObserver(nums);
// ... Parsing of file - works fine
return nums;
}
Currently this runs and my println from update() in PhoneBookObserver is output.
My questions are:
To directly answer your question, I would probably implement the Observer
as an inner class in the controller. Then it has access to everything in the controller.
Assuming here PhoneBook
defines a method of the form
public List<PhoneNumber> getPhoneNumbers() ;
then you could do:
public class Controller {
@FXML
private ListView<PhoneNumber> phoneNumberList ;
private PhoneBook numbers = new PhoneBook() ; // or initialize from elsewhere
public void initialize() {
numbers.attach(new PhoneBookObserver(numbers));
// ...
}
private class PhoneBookObserver extends Observer {
PhoneBookObserver(PhoneBook numbers) {
this.numbers = numbers ;
}
@Override
public void update() {
phoneNumberList.getItems().setAll(numbers.getPhoneNumbers());
}
}
}
Note that in
public abstract class Observer
{
public PhoneBook numbers;
public abstract void update();
}
the field numbers
really serves no purpose, as the only method doesn't use it. So you could remove it (subclasses can define such a field if they need). Then you may as well make it an interface, and since it only has one method, it's a @FunctionalInterface
:
@FunctionalInterface
public interface Observer {
public void update() ;
}
and now it can be implemented with a lambda expression, so the implementation is so thin that you basically stop having any issues with "accessing the UI":
public class Controller {
@FXML
private ListView<PhoneNumber> phoneNumberList ;
private PhoneBook numbers = new PhoneBook() ; // or initialize from elsewhere
public void initialize() {
numbers.attach(() -> phoneNumberList.getItems().setAll(numbers.getPhoneNumbers());
// ...
}
}
Finally, note that JavaFX Properties and observable lists basically already provide an implementation of the observer pattern, so you're pretty much reinventing the wheel here. You could just have
public class PhoneBook {
private final ObservableList<PhoneNumber> numbers;
public ObservableList<PhoneNumber> getPhoneNumbers() {
return numbers ;
}
}
and then
public class Controller {
@FXML
private ListView<PhoneNumber> phoneNumberList ;
private PhoneBook numbers = new PhoneBook() ; // or initialize from elsewhere
public void initialize() {
phoneNumberList.setItems(numbers.getPhoneNumbers());
}
}
and the list view will observe the (already-observable) list of numbers for you. There is no real need for your Observer
or PhoneBookObserver
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With