When implementing the Observer pattern, there are two main approaches to consider: the 'push' model and the 'pull' model.
In the 'push' model, the subject (i.e. the Observable) sends the observer on notification all the data it will need. The observer doesn't need to query the subject for information. In the 'pull' model, the subject merely notifies the observer that something happaned, and the observer queries the subject based to get the information it needs.
Let's discuss the pros and cons of both approaches:
Push
The main advantage of the 'push' model is lower coupling between the observer and the subject. The observer doesn't need to know anything about the subject in order to query it. If it needed to, we'd need to do one of the following: A- do downcasting on the side of the observer in order to invoke class-specific get
methods on the subject. This is bad. B- make the Observable
interface more class-specific, offering specific get
methods, thus making the relationship between the observer and subject less general and making things more copupled.
By implementing the 'push' model, we avoid all of this.
However the disadvantage is less flexibility: the subject may not always know what exact information the observers need in order to send it to them. This will often mean more specific Observer intefaces, such as AgeObserver
that are notified when the 'age' of the subject is changed, and HeightObserver
which are sent the current height
of the subject on notification.
This is one option. The other is the subject sending a whole lot of information encapsulated in an Info
object of some sort and have the observers query it from there. But again, we can't be sure we're sending the correct info. So it's either this, or forcing the observers to implement more specific Observer interfaces, which tightens the coupling on the observer's side.
Pull
I already noted the disadvantages of the 'pull' model. The observers would have to know things about the subject in order to query the right information, which leads A- to downcasting (ugly), or B- favorably to more specific Observable
interfaces, that offer more specific accessor methods. For example AgeObservable
offers a getAge()
method.
The advantage of this is more flexibility. Each observer can decide for itself what to query, without relying on the subject to send the correct information.
You should choose the strategy that is better for the specific project you're working on.
In reality, you'll always have specific Observer
and Observable
intefaces, so you have some coupling between the sides anyway.
So choose either 'pull' or 'push' by what fits you best.
Do all AgeObserver
s need simply the age
of the subject? Implement the 'push' model. Less coupling and simpler.
Do all HeightObserver
s need varying information about the subject - aka one needs to query the age too, and some other object needs to query the weight in addition to the height? Implement the 'pull' model. This would force you to add accessors (getters) to the Observable
inteface (either this or pass the actual object as a parameter in it's explicit type, but doing this through the interface allows you to deny the observers access to things that don't matter to them). This soultion creates higher coupling, but it's more flexible.
Choose what fits your situation best.
arg
is a parameter or group of parameters, as an additional option