Observer pattern: Prevent multiple firings on changed internal property
https://softwareengineering.stackexchange.com/questions/274529
-
07-10-2020 - |
Question
I have an object Foo
that has 2 public properties: A
and B
.
These properties can be changed from outside, as the application I'm working on is using a data-binding architecture. This is the front-end (client) code, but it's also communicating with a server to fetch data.
So my object is listening for changes to both A
and B
.
Any change in A or B is triggering the update()
method, which triggers a request to the server.
I also have a piece of logic in my object Foo
that says every time you change B
, you have to reset A
to its initial value.
My problem is, how do I deal with updating B without triggering the update()
method twice?
Looking at the image above, the green path should always trigger an update. The red path ends up sometimes triggering 2 updates, because by changing B it changes A (if it's not the initial value), which always triggers an update.
I have to add that everything is asynchronous so the observers for my properties don't notify me immediately after changing objects. (design choice of the platform and language I'm working in – Polymer.JS). So this makes it harder to set a flag before changing B
from A
- I tried this but it didn't work, as the watcher method gets triggered after the chain of changes has executed.
As I come from the front-end world, with a background in graphic design, I don't have the knowledge necessary to spot the mistakes I might be doing, but I want to learn, so I'd like you to point me to reference material and/or books I can read about the correct applications of the observer pattern.
Is there a pattern that deals with this particular scenario and what are possible solutions to this?
If the observer pattern isn't the right choice for this, is there another pattern I could use internally that allows me to expose only the two properties A
and B
to the outside world?
A third option would be that I'm applying the right pattern, but the platform doesn't support what I'm trying to achieve. I'd like to know this so I can post the question on StackOverflow under the right tags.
La solution
You make a special message to A that changes it in a way that is not propagated. Its a common idiom where you want general updates to send notifications, but not internal updates or refreshes.
For example, I have a dialog that has notification handlers for changes to various controls, but in the initialisation I want to set the controls initial state - without sending these notifications. In this case, I simply set an 'initialising' flag that the notification handler checks for, if its set the handler simply does nothing.
If you cannot set a flag to change the state of A to prevent notification, then you will have to send it with the change message. So you have 2 messages, one to update A (the general case) and another to quietly change A that is sent by B but still allows for changes to A to be triggered when others change it. I don't know how you make these updates so the implementation specifics is up to you.
Autres conseils
Generally you would have values A and B hidden using a mixin, changeable only through a method setA and setB (or better still by using a setter function). Internally, you would simply assign A or B directly without calling the setA or setB directly, and then call update() which triggers the event.
In other words, you'd have setA that assigns A and then resets the value of B without calling setB. Take a look at this quick example:
function Obj() {
var A, B;
function resetB() {
console.log('Resetting B');
B = '';
}
function update() {
console.log('Values updated');
}
return {
get A() { return A; },
get B() { return B; },
set A(aVal) {
A = aVal;
resetB();
update();
},
set B(bVal) {
B = bVal;
update();
}
};
}
var obj = new Obj();
obj.B = 'test';
// Console output:
// Values updated
// assert(obj.B == 'test')
obj.A = 'me';
// Console output:
// Resetting B
// Values updated
// assert(obj.A == 'me')
// assert(obj.B == '')