nus-cs2103-AY2021S1 / forum

20 stars 2 forks source link

observer pattern: why is the interface grouped with the class to be observed? #496

Closed nicktohzyu closed 3 years ago

nicktohzyu commented 3 years ago

Example from textbook image

Example from AY14/15S2 paper image

In both these cases, it seems that the interface is grouped with the class to be observed (data/students) rather than the observer. why is this the case? I would instinctively group it with either the observer, or put the interface outside of a grouping

wltan commented 3 years ago

The 'observed' object (e.g. StudentList in the first example) is closely related to the Observer interface, because the observed object is responsible for managing and notifying the Observers whenever something happens to itself.

On the other hand, the 'observing' object can come from any component in the project, as long as it is interested in listening for changes in the observed object. It wouldn't make sense to put Observer with the observing objects, as they may be scattered across multiple different packages.

damithc commented 3 years ago

The 'observed' object (e.g. StudentList in the first example) is closely related to the Observer interface, because the observed object is responsible for managing and notifying the Observers whenever something happens to itself.

On the other hand, the 'observing' object can come from any component in the project, as long as it is interested in listening for changes in the observed object. It wouldn't make sense to put Observer with the observing objects, as they may be scattered across multiple different packages.

That's correct. The observed are the ones in control. They specify the 'contract' (i.e., the interface) the observers need to follow. If not, the observed becomes dependent on an outside component, which defeats the purpose of the pattern.

nicktohzyu commented 3 years ago

I see, in this case might it be more appropriate to call the interface StudentListObserver?

nicktohzyu commented 3 years ago

Also, wouldn't the observer need a reference to the observed? Shouldn't this be represented as a solid arrow from observer to observed?

wltan commented 3 years ago

I see, in this case might it be more appropriate to call the interface StudentListObserver?

I suppose that would be a more apt name to make it less ambiguous and more readable, in case there are multiple instances of the observer pattern throughout the project.

Also, wouldn't the observer need a reference to the observed? Shouldn't this be represented as a solid arrow from observer to observed?

The observer does not need a reference to the observed. Once added to the observed, the observer does not need to keep track of anything else as the observed is responsible for updating the observers. Furthermore, the entire purpose of the observer pattern is to eliminate coupling between observer and observed.

If the observer needs to observe some state of the observed the state change can be expressed as a parameter to the update method in the interface, in which case there may be a dependency from the observer interface. Example: ListChangeListener and ListChangeListener.Change

damithc commented 3 years ago

@wltan' s answer is correct. Just a clarification about the below:

Furthermore, the entire purpose of the observer pattern is to eliminate coupling between observer and observed.

The pattern eliminates the dependency from the Observed to the Observers. The dependency in the other direction still exists as the new interface in the middle is considered a part of the Observed (but it's not as bad as a direct dependency on the Observed). As @wltan mentioned, the Observers receive all the info they need via the update method.

nicktohzyu commented 3 years ago

the Observers receive all the info they need via the update method

How would this work since the update method is nullary?

wltan commented 3 years ago

As seen in the ListChangeListener example from JavaFX, the update method (in this case onChanged) isn't necessarily nullary. If the observer requires additional information, that can be included as part of the interface's dependencies.

damithc commented 3 years ago

As seen in the ListChangeListener example from JavaFX, the update method (in this case onChanged) isn't necessarily nullary. If the observer requires additional information, that can be included as part of the interface's dependencies.

That's correct. The given example is the simplest possible Observer interface. It can be made more sophisticated as necessary.

seanjyjy commented 3 years ago

The pattern eliminates the dependency from the Observed to the Observers

Hi @damithc . Why does it eliminates the dependency from the Observed to the Observers and not the opposite? If for example taking the code that is in the chapter itself, it is studentList.add(observers), isn't this a dependency from the Observed to the Observers?

damithc commented 3 years ago

Hi @damithc . Why does it eliminates the dependency from the Observed to the Observers and not the opposite? If for example taking the code that is in the chapter itself, it is studentList.add(observers), isn't this a dependency from the Observed to the Observers?

studentList.add(observers) the parameter to this method is the Observer interface (which is provided by the Observed side), not the concrete classes that are doing the actual observing (which are 'outsiders' that the observed doesn't want to depend on). And that makes the difference.

In the example below, it's addUi(Observer), not addUi(StudentListUi). That means the StudentList (i.e., the observed) doesn't know anything about the outside observers (e.g., StudentListUi) and yet it can inform those observers when the data changes. image

This point is also explained in the lecture video here https://nus-cs2103-ay2021s1.github.io/website/schedule/week11/topics.html#design-design-patterns-observer-pattern-what

nicktohzyu commented 3 years ago

As seen in the ListChangeListener example from JavaFX, the update method (in this case onChanged) isn't necessarily nullary. If the observer requires additional information, that can be included as part of the interface's dependencies.

Thanks, I understand this part, since information is passed as Change into the method. What i'm wondering about is whether a nullary update would work?

damithc commented 3 years ago

Thanks, I understand this part, since information is passed as Change into the method. What i'm wondering about is whether a nullary update would work?

In some cases, the observer just need to know something changed, not necessarily what changed e.g., to invalidate a cache. In such cases, no need to send state info via a parameter.

nicktohzyu commented 3 years ago

thanks!