The Elegant way to handle Cyclic Event in Java?
Going back to Model-View-Controller, think about what your Model is, and what your View is.
In your current implementation, you have two models (one for each Spinner control), and they're being synced through the View layer.
What you should be doing though is share the same backing model. For the spinner with a subtracted value, create a proxy to the original model. ie:
class ProxySpinnerModel implements SpinnerModel {
getValue() { return originalSpinner.getValue() - 10 }
setValue(v) { originalSpinner.setValue(v+10) }
}
spinnerA = new JSpinner()
spinnerB = new JSpinner( new ProxySpinnerModel( spinnerA.getModel() ) )
Now, you don't need to add listeners, since they're both working off the same model and the default implementation (the originalModel) already has change listeners which it fires to the view.
Problem Solved
I've got many different suggestions. Particularly, i want to thank Marc W & Reverend Gonzo. I'm here to make a summary for these ideas; this can save your time navigating thru big chunk of texts.
This problem can be easily bypassed if you carefully decouple the View and Model-Controller.
The dead cycle is caused by dependent writes: write_1 -> write_2 -> write_1 ->...
. Intuitively, breaking the dependency can solve the problem elegantly.
If we look into the problem in depth, we can find updating the corresponding views doesn't necessarily involves an external write call. Actually, a view only depends on the data it's representing. Known this, we can then re-write the logic as follow: write_1 -> read_2 & write_2 -> read_1
.
To illustrate this idea, lets compare the 3 methods mentioned by different posters: alt text http://www.freeimagehosting.net/uploads/2707f1b483.png
As you can see, only the proxied view can solve all the dependency thus it's the generic solution for this knid of problem.
In practice, it can be implemented as something like this (in your event-response codes):
setValue(newValue);
anotherSyncUI.parse(); // not anotherSyncUI.setValue() any more
anotherSyncUI.repaint();
No more loops. Solved.
It's a bit complicated, but you could make A
actually be an object that's observable. Both spinners (or whatever needs to update itself based on A
's value) would then observe A
. Whenever A
changes, the spinners (or again, whatever object) update themselves to reflect the new value of A
. This decouples the spinners' logic from one another. In your example here, the spinners should not be coupled to one another because they really have nothing to do with each other. Instead, they should both simply be bound to A
and take care of their own view updating individually.
Whenever the value in the first spinner is changed, you would simply update A
's value to match it. Whenever the value in the second spinner is changed, you would of course add 10 to its value before assigning it to A
.
Update
In response to the update to your original question, my answer is that the spinners do not listen to one another's change events. Have a separate event handling method for each spinner. A user clicking the up or down arrows in the spinner generates a different event than calling setValue
on the spinner programmatically, correct? If the spinners are completely independent of one another, there will be no infinite loop.