Skip to main content

Observer delivery is a contract

This lesson uses the behavioral-contracts.zip runtime comparison API. Its policies differ deliberately from core behavioral-patterns.zip; see the contract comparison.

What this concept is​

Observer decouples notification from concrete consumers, but does not define ordering, cancellation, nested publication or exception behavior for you. Those decisions belong in an explicit contract.

Why it matters here​

A class diagram can be correct while callbacks are skipped, duplicated or retained after disposal. This extension turns the original observer exercise into a reproducible behavioral test.

Concrete example​

Our synchronous publisher assigns increasing subscription tokens. At publish start it snapshots token IDs in registration order. Before calling each token, it checks whether that subscription is still active. If A removes B before B’s turn, B does not run. If A adds C during delivery, C waits for the next publication. Registering the same callback twice creates two independent subscriptions.

Common confusion / misconception​

Copying the listener list is not a complete cancellation policy. A snapshot of callbacks would keep B eligible after removal; our snapshot of IDs plus a live check cancels it. Both can be defensible, but they are observably different contracts.

How to use it​

The lab accepts immutable string events. It rejects recursive publish on the same publisher with RuntimeError, collects ordinary callback Exceptions as (token,type-name) pairs, and continues to later callbacks. It resets its publishing flag in finally. It does not catch BaseException such as KeyboardInterrupt. These are local design choices, not guarantees implied by the Observer pattern or by another framework.

Check yourself​

A publishes recursively without catching the rejection. Which outer callback error is recorded, and do remaining outer subscribers run?

Mini drill or application​

Trace A removing B and adding C on the first publication: first trace A, second trace A then C (assuming A does not repeat the mutation). Add an independent failing callback and show the continued delivery order. Explain why a queue would need a different recursion and scheduling contract.

Read this only if stuck​

Use the existing book/reference map and runtime reading guide. The Design Patterns book index supplies pattern vocabulary; the local lab contract specifies its actual semantics.