Whether C++ or JAVA, a Notification to the observer can come along with the information of what is changed. The same methods notifyObservers(Object arg) can also be used in C++ as well.
Generally, the issue will remain is that there could be multiple subjects dispatching to one or multiple observers and hence, the class arg
cannot be hard coded.
Usually, the best way to do is to make arg in the form of generic message/tokens that forms the same data type for various classes but values differ for different observed classes. Alternatively, if all such notification values are derived out of the class on some based class which is common to all.
For observer pattern, it is important that Arg data type is not hard coded between the observee and observer - else it is a coupling that makes things difficult to evolve.
EDIT
If you want that observer not only observes but also needs to do a lot of other tasks based on the what has changed then, you can also check out Visitor pattern. In visitor pattern, the observer/visitor calls the modified object and hence can not only know what the modification is but can actually work on it
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.
Best Answer
Having a "Map object that keeps track of all the cells to which it can travel" looks to me like an example of premature optimization.
Instead of remembering all those cells for each piece "just in case it could become slow", why not give a piece a method with the board as parameter, which returns the related
List<Cell>
(orMap<Direction, LinkedList<Cell>>
) by calculating it when the method is called? This way, no notifications or observer pattern or "32 listeners" are needed any more. It may be debatable if it should really be a method of the piece, or a method of the board. However, if you put this method into the board instead of the piece, pieces will not have to know anything about the board, so this avoids a cyclic dependency between boards and pieces.If it turns out later that this method is called quite often for the same, unchanged board so it becomes a performance bottleneck (which I doubt), you can still cache its results using memoization. That would lead to a solution where a
Map<Direction, LinkedList<Cell>>
per piece is required, but without the necessity of actively observing other board changes.