patternjavaModerate
Listening to modifications on variables
Viewed 0 times
variablesmodificationslistening
Problem
I am seeking advice on the efficiency and long term implications of various ways of capturing modifications to instance variables that I have tried implementing. Essentially, this is setup:
In my framework, I originally had a class whose sub classes were only supposed to contain public instance variables - kind of like a
Now, however, I need a way to individually listen for modifications to each of the instance variables. For example, when someone modifies the rotation field of a
My initial "solution" was an absolute hack. I essentially spin polled the variables I was interested in, stored the previous value, and examined if the new value matched the old one.
My first thought at remedying this was to change my setup from public instance variables to private variables with getters and setters for access (which I didn't do earlier for simplicity and code compactness). I would also maintain a list of listeners in every struct and notify each one when a setter was called.
However, this approach fell flat for two reasons:
First of all, what if a 3rd party extending this class forgot (or simply didn't know) to notify the listeners? How would I guarantee that they would implement the class in a way that wouldn't break other parts of the framework depending on these events? Another problem was that of mutable objects - even if clients correctly implemented getters and setters by notifying the listeners when a setter was called, there was no way of detecting when an object was retrieved with a getter and tinkered with internally.
My current approach seem
In my framework, I originally had a class whose sub classes were only supposed to contain public instance variables - kind of like a
struct in C. These classes were pretty much just for convenience in wrapping up multiple objects and primitives into one object.Now, however, I need a way to individually listen for modifications to each of the instance variables. For example, when someone modifies the rotation field of a
Transform object (featuring position, rotation, etc.), ideally a list of listeners would all be fired with some event that only pertains to the rotation variable - I don't care about the variables that weren't modified. Of course, you could add and remove listeners from this list.My initial "solution" was an absolute hack. I essentially spin polled the variables I was interested in, stored the previous value, and examined if the new value matched the old one.
My first thought at remedying this was to change my setup from public instance variables to private variables with getters and setters for access (which I didn't do earlier for simplicity and code compactness). I would also maintain a list of listeners in every struct and notify each one when a setter was called.
However, this approach fell flat for two reasons:
First of all, what if a 3rd party extending this class forgot (or simply didn't know) to notify the listeners? How would I guarantee that they would implement the class in a way that wouldn't break other parts of the framework depending on these events? Another problem was that of mutable objects - even if clients correctly implemented getters and setters by notifying the listeners when a setter was called, there was no way of detecting when an object was retrieved with a getter and tinkered with internally.
My current approach seem
Solution
In my framework, I originally had a class whose sub classes were only supposed to contain public instance variables - kind of like a struct in C.
^^^ That, that's the source of your problem ^^^
Java is an object oriented language. Objects encapsulate their data, and the better Object designs make it impossible for other classes to directly access the internal data.
Your system, you say, has a Transform class, with some fields including a
And, anyone can change the rotation.
The standard system used in Java is to make that
Now, this is an encapsulated object.
To extend this object, so that interested parties can 'listen' for rotation events, you can have listeners registered with the class, with 'callbacks' that get fired when the rotation changes.... This is called the Observer Pattern. You should take a moment and read through all the common patterns, and you will discover a number of tools you can use for common problems.
and, your class would have a listener mechanism added:
Almost the entire Swing GUI framework has been built using this system. Consider all the Listeners available on JComponent and all the Events that can happen. Each Event has a corresponding listener available.
^^^ That, that's the source of your problem ^^^
Java is an object oriented language. Objects encapsulate their data, and the better Object designs make it impossible for other classes to directly access the internal data.
Your system, you say, has a Transform class, with some fields including a
rotation. It looks something like:public class Transform {
public double rotation = 0.0;
....
}And, anyone can change the rotation.
The standard system used in Java is to make that
rotation private, and to provide getters, and setters for it:public class Transform {
private double rotation = 0.0;
public void setRotation(double newrotation) {
this.roation = newrotation;
}
public double getRotation() {
return rotation;
}
....
}Now, this is an encapsulated object.
To extend this object, so that interested parties can 'listen' for rotation events, you can have listeners registered with the class, with 'callbacks' that get fired when the rotation changes.... This is called the Observer Pattern. You should take a moment and read through all the common patterns, and you will discover a number of tools you can use for common problems.
public interface RotationListener {
public void rotationChangedEvent(double oldrotation, double newrotation);
}and, your class would have a listener mechanism added:
public class Transform {
private double rotation = 0.0;
private final List rotationListeners = new ArrayList<>();
public void setRotation(double newrotation) {
double old = rotation;
this.roation = newrotation;
for (RotationListener ping : rotationListeners) {
ping.rotationChanged(old, newrotation);
}
}
public double getRotation() {
return rotation;
}
public void addRotationListener(RotationListener toadd) {
rotationListeners.add(toadd);
}
....
}Almost the entire Swing GUI framework has been built using this system. Consider all the Listeners available on JComponent and all the Events that can happen. Each Event has a corresponding listener available.
Code Snippets
public class Transform {
public double rotation = 0.0;
....
}public class Transform {
private double rotation = 0.0;
public void setRotation(double newrotation) {
this.roation = newrotation;
}
public double getRotation() {
return rotation;
}
....
}public interface RotationListener {
public void rotationChangedEvent(double oldrotation, double newrotation);
}public class Transform {
private double rotation = 0.0;
private final List<RotationListener> rotationListeners = new ArrayList<>();
public void setRotation(double newrotation) {
double old = rotation;
this.roation = newrotation;
for (RotationListener ping : rotationListeners) {
ping.rotationChanged(old, newrotation);
}
}
public double getRotation() {
return rotation;
}
public void addRotationListener(RotationListener toadd) {
rotationListeners.add(toadd);
}
....
}Context
StackExchange Code Review Q#56294, answer score: 11
Revisions (0)
No revisions yet.