Observer/Observable PatternBasic Ideas:
- The Observer pattern is used to keep consistency between related objects
while minimizing their coupling and maximizing reusability (or independence)
of the objects.
- Creates a one-to-many relationship between a subject and its observers.
All observers are notified when there is a change to the subject. The
observers then query the subject to synchronize themselves.
When to
use this pattern:
- When a change to one object requires the change of a variable number of
other objects (not necessarily known at compile-time).
- When an object should be able to talk to another object, but you don't
want them essentially dependent on each other.
Benefits:
- Can add new subjects and new observers all independently of all other
subjects and observers.
- Minimal dependence between subject and observer
- Subject doesn't need to know how many observers it has. It simply
broadcasts a change in state.
Pitfalls:
- Because observers don't know about each other, a simple update to an
observer might cause a long chain of other updates.
- Each observer decides whether it needs to update something when it
receives the notification from the subject that something has changed.
Therefore, complicated observers have to do a lot of work to figure out what
changed when they receive a notification.
Implementation
Issues:
- How to keep track of the subject to observer mapping?
- Hash table: simple and best use of storage.
- How to observe more than one subject?
- Pass reference of the subject when making notification so the observer
knows which subject to check.
- Who calls Notify?
- Subject: every time the subject sees a change it calls notify. However,
this may lead to many unnecessary calls if multiple things are changing at a
time.
- Observer: when observer is finished with updates, can call notify. This
will minimize the number of notify calls made, but forces the observer to
remember to call notify after every change is made.
- When a subject is deleted, what happens to its observers?
- Subject should notify its observers, so they can delete reference to it.
- Make sure subject is self-consistent before calling notify.
- Make "notify" the last statement in an abstract class's template method
- How much information to give an observer when notifying of change?
- Push model: give the observer detailed information about the change good
because makes work easy for the observer. Bad because makes the code less
reusable and assumes knowledge about the structure and needs of the
subject's observers.
- Pull model: gives minimal change information. Makes life hard for the
observers; they must figure out what changed with no help from the subject.
- Find a happy median.
- Limit update calls that don't affect a particular observer?
- When an observer registers with a subject, also tells the subject of
which types of events it wants to be notified. Can create an Aspect class
whose objects are passed on registration with a subject.
Handling complex relationships in updates with Change
Managers:
- Can use a Change Manager to interface with particularly complex updates.
- Keeps track of the subject/observer mapping, eliminating need for
subjects and observers to keep references of each other.
- Defines update strategy
- Updates observers when subject notifies Manager of a change.
- Simple Managers may just update every registered observer upon
notification from a subject.
- If observers are dependent upon multiple subjects, observers may receive
redundant update notifications. A more complex Manager can keep track of
this information and ensure that only one update is received per Observer.
- Note: Change Manager is an instance of the Mediator pattern.
Pros and cons of different update semantics
| Semantic \ Qualities |
Design abstraction (i.e. decoupling) |
Efficiency |
Simplicity |
| Push |
bad |
good |
good |
| Pull |
good |
bad |
good |
| Change Manager |
good |
good |
bad |
|