How do we properly track changes to an object's properties in Java (specifically JavaFX)?
My application allows users to modify the properties of the underlying data model objects. When the user clicks on a "Save" button, I want to save the new state of the object to a database. However, if the user clicks on "Cancel," I need to revert back to the object's original state.
Consider the following example objects (getters omitted for clarity):
class Person {
private final StringProperty name = new SimpleStringProperty();
private final StringProperty email = new SimpleStringProperty();
private final SimpleListProperty<PhoneNumber> phoneNumbers = new SimpleListProperty<>();
public Person(String name, String email, ObservableList<PhoneNumber> phoneNumbers) {
this.name.set(name);
this.email.set(email);
this.phoneNumbers.set(phoneNumbers);
}
}
class PhoneNumber {
private final IntegerProperty areaCode = new SimpleIntegerProperty();
private final IntegerProperty prefix = new SimpleIntegerProperty();
private final IntegerProperty lineNumber = new SimpleIntegerProperty();
public PhoneNumber(int areaCode, int prefix, int lineNumber) {
this.areaCode.set(areaCode);
this.prefix.set(prefix);
this.lineNumber.set(lineNumber);
}
}
Ideally, I want the GUI to load the original Person object, and make a copy of it to bind to the UI controls (even if I need to write this method myself):
Person editedPerson = copyOf(originalPerson);
Then, if the user clicks "Cancel," do nothing. Upon clicking "Save," however, set originalPerson to be equal to editedPerson.
I have looked into cloning objects, but the general consensus seems to recommend against that as it does not ensure the original object is not changed. Also, unless doing a deep copy, any objects references within Person, for example, would not be properly copied.
The other option I've seen is to use a copy constructor but my real-world application uses much more complex objects than the sample above. There are several levels of objects nested within each object and manually copying the entire hierarchy seems like overkill.
So what is the main question? Is there already an API available (or 3rd party library) that handles this functionality? It seems to be a pretty standard expectation for a user to be able to revert their changes.
Related
My JavaFX application should look like this:
Now I want to make sure that the detail view adapts as soon as I select another person from the table view.
My classes so far:
public class Person {
private final StringProperty name = new SimpleStringProperty();
private final StringProperty title = new SimpleStringProperty();
private final IntegerProperty age = new SimpleIntegerProperty();
public Person(String name, String title, int age) {
setName(name);
setTitle(title);
setAge(age);
}
// Getters and Setters
}
public class PresentationModel {
private final ObservableList<Person> persons = FXCollections.observableArrayList();
private final ObjectProperty<Person> selectedPerson = new SimpleObjectProperty<>();
public PresentationModel() {
// add some users to persons list
}
// Getters/Setters
}
In the UI class with the table I have set up a listener like this:
personsTable.getSelectionModel().selectedItemProperty().addListener((observable, oldPerson, newPerson) -> {
model.setSelectedPerson(newPerson);
});
And in the UI class with the details view I have set up a binding:
nameLabel.textProperty().bind(model.getSelectedPerson().nameProperty());
The PresentationModel model attribute is created once with the start of the application and than passed through the constructors to all the UI classes.
But this bind is not working as expected.
What can I change so the binding works correctly and the property changes?
The binding doesn't work, because getSelectedPerson just returns the current selected person, and isn't recomputed if the selected person changes.
Using just the standard API, you can do
nameLabel.textProperty().bind(Bindings.selectString(
model.selectedPersonProperty(), "name"));
This API is a little unsatisfactory in a number of ways. For one, there is no compile-time checking that the selectedPersonProperty() has a nameProperty(), and that it is of the correct type. Secondly, it uses reflection, which doesn't perform well in the case that you call it very frequently (which does not apply here). Finally, if the selected person is null, this will dump lots of superfluous warnings to standard output (despite the fact that the API documentation indicates this is a supported use case!!!).
An alternative is provided by the ReactFX framework:
nameLabel.textProperty().bind(Val.selectVar(
model.selectedPersonProperty(), Person::nameProperty));
I have an object that I want to populate with information. I retrieve the information from a number of different services. I made a helper class that has one public method and then has a number of private methods that do the work to call the services. What I have written works fine but I'm not sure if it is the correct way to do this.
You may be wondering why I need an object holding all this information. I need it all in one object because I create a json object from this java object and pass that to the javascript layer.
What is wrong with my approach and is there a programming paradigm I should be following to do something like this?
Example:
Person object with getters and setters for firstName, lastName, age, height, weight, list of favourite foods, list of favourite countries, list of comments.
Service 1 gives firstName, lastName, age, height and weight
Service 2
gives list of favourite countries and list of favourite foods
Service
3 gives a list of the comments made by the person
I have a personHelper class that looks like this:
public class PersonHelper{
public Person getPerson(userDetails){
Person person = new Person();
this.setPersonDetails(person, userDetails);
this.setFavourites(person, userDetails);
this.setComments(person, userDetails);
return person;
}
private Person setPersonalDetails(Person person, UserDetails userDetails){
returnedObj = callToService1(userDetails);
person.setFirstName(returnedObj.getFirstName());
person.setLastName(returnedObj.getLastName());
person.setAge(returnedObj.getAge());
person.setHeight(returnedObj.getHeight();
person.setWeight(returnedObj.getWeight());
return person;
}
private Person setFavourites(Person person, UserDetails userDetails){
<List>favsList = callToService2(userDetails);
person.setFavourites(returnedObj.getFavs(favsList));
return person;
}
private Person setComments(Person person, UserDetails userDetails){
<List>commentsList = callToService3(userDetails);
person.setComments(returnedObj.getComments(commentsList));
return person;
}
}
and then in my controller I call
person = personHelper.getPerson(userDetails);
jsonResponse = jsonProcessor.writeAsString(person);
return jsonResponse; // returns the ajax response to js
Thanks in advance for any help or suggestions.
EDIT: After more research I found that the object I am populating is referred to as a Data Transfer Object and I am populating it using the Java Bean method.
There's a trend these days to limit the mutability of objects so your setter-based approach, although workable, is sometimes not seen as the best way to create an object, even a data transfer type of object. One other thing to consider is how many objects know about each other and how much they know - it seems your PersonHelper class needs to know pretty much everything about UserDetails and Person. So if you add a field to Person, you need to add it to UserDetails and also add to PersonHelper to get that field populated.
For your type of object, you might find the Builder pattern useful. A builder is a short-term transient object designed to gather data for construction. Often the builder will have a fluent API, and gets passed to the (private) constructor of the transfer class. That means that all your code responsible for building the object is clear that that is its responsibility because it works with a Builder. Meanwhile, the constructed transfer object is effectively immutable and it becomes significantly easier to reason about the thread-safety of your code and to understand what values something might have at different parts.
public class Person {
private final String firstName;
private final String lastName;
private Person(final PersonBuilder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
}
... usual getters etc ...
public static class PersonBuilder {
private String firstName;
private String lastName;
private PersonBuilder() {
}
public PersonBuilder withFirstName(final String name) {
this.firstName = name;
return this;
}
public PersonBuilder withLastName(final String name) {
this.lastName = name;
return this;
}
public Person build() {
return new Person(this);
}
}
public static PersonBuilder newPerson() {
return new PersonBuilder();
}
}
In this example the builder is a little over-wieldy, but when you've got twenty or thirty different pieces of data which are somehow optional it can make sense and makes for very easy to read construction code...
Person.newPerson().withFirstName("Sam").withLastName("Spade").build()
It seems to me that your 'UserDetails' object could be turned into a kind of builder. And so your 'PersonHelper' class would end up just calling userDetails.build() rather than knowing all about what fields the Person object (and userDetails object) contains.
There is no general paradigm for your question, but here are a few tips for your design:
It seems that your person data (names, favourites) is distributed among several data stores and you have to gether it all in your PersonHelper class. I don't know if this services are used anywhere else, but from the controller point of view this helper should be a service too.
Since your service invocations are independent, you can execute them in parallel
For some kind of applications it can be even better if you expose these services for UI level. For example, if data is presented in different UI blocks, client can make several asynchronous requests and display the data as soon as responses are received.
can someone please tell me why I cannot use the Set properly? I managed with the Get to load the Name, Surname etc.. in the text boxes but the Set is not letting me read from the text boxes and save them. Thanks
custnameTF.setText(bookingfile.getCustomer().getPersonName());
custsurnameTF.setText(bookingfile.getCustomer().getPersonSurname());
customerbooking.setCustomer(.setPersonName(custnameTF.getText));
public class Booking implements Serializable{
private String flighttime;
private String flightlocation;
private String flightfee;
private boolean car;
private boolean insurance;
private Customer customer;
I'm trying to connect 2 classes together without an extend. I want to load and save on the Customer class through the Booking Class. Thanks
First in hand, that's not the correct syntax to set.
Since that setCustomer accepts only Customer object, you need to create a Customer object and then set it to bookingCustomer.
Customer customer= new Customer();
customer.setPersonName(custnameTF.getText); // look at the correct syntax.
//set remaining properties to customer objects from text fields
// ..
//then
customerbooking.setCustomer(customer);
Setters don't return anything in general, which is why you can't call another set method with the return value of another set method (returns void) as the argument.
I have the following BO which is already there in the system
public class userBO
{
private String userId;
private String password;
private String firstName;
private String midName;
private String lastName;
private String userType;
private String userDepartment;
private String userAuthority;
//There are some more fields
//getter and setter
}
Now I want to built a dropdown in which I will display Name (firstName + lastName) and will use userId as value. So for that I will make a list of object.
So my question is should I use the existing userBO class or should I create new class something like below
public class userDropDwonBO
{
private String userId;
private String firstName;
private String lastName;
//getter and setter
}
I want to know the answer from Good Architect point of view and also performance point of view, Will there be any better performance if I user new userDropDownBO
userDropDownBO object will definitely use less memory than the above class.
It is because all your members are private intance variable, everytime a constructor is invoked, a set of all private variables will be created on stack and will be initialized to their default values so will consume more memory and initialization time.
But it solely depend on your requirement:
If other fields are required other than these three fields go for the userBO class.
If other fields are unnecessary but no of objects to be created are small in number, go for userBO.
If other fields are unnecessary but no of objects to be created are very large in number, go for userDropDownBO.
Its a personal opinion and rest is your choice.
If you are going to create a new class beside the existing one named UserBO just for the sake of binding it to the JComboBox, that will definitely be a waste of memory and waste of time as well and also you will need to provide an additional logic to map your original object of type UserBO to the the object of type UserDropDownBO.
I would say that your approach maybe applicable in case the BO itself is so complex in dealing with, so that you need to create a separate model to be used in the drop down box.
I wish to try to make an array reference a class but can not think of a way to do this.
I have my main class, client and another called organisation.
Im basically making a small database where the user is able to add an organisation and also edit the info in the organisation. Info including, ID#, name, address, ect
I d like to know how i can get an array to basically store new organisation class's so that when i want to add an organisation it runs a new organisation class and stores that version of the class, in the array.
You may be best using a List, unless you can be sure of the maximum number of organisations that can be associated with a client. The size of an array is fixed at the point it is created, which may not be appropriate for your use case.
For example:
public class Client {
private final List<Organisation> organisations = new ArrayList<Organisation>();
void addOrganisation() {
// dummy/example method
organisations.add(new Organisation("Foo", new Address("blah")));
}
}
public class Organisation {
private final String name;
private final Address address;
public Organisation(String name, Address address) {
this.name = name;
this.address = address;
}
}
ArrayList<Organisation>
is the best option I think. read the documentation to get familiar with it. it's quiet handy.