Reactive Events: From Observer to Flow
The **Observer Pattern** defines a one-to-many dependency where state changes in a "Subject" (Publisher) are automatically broadcast to "Observers" (Subscribers). In modern high-throughput systems, the pattern has evolved into **Reactive Streams** to handle asynchronous data flow and **Backpressure**.
I. Evolution: From Callbacks to Streams
* **Classic GoF:** Synchronous method calls. If an observer is slow, the entire subject is blocked.
* **Java Flow API (Java 9+):** Standardized interfaces (`Publisher`, `Subscriber`, `Subscription`) that support asynchronous, non-blocking flow with flow control.
II. The Backpressure Problem
In a push-only observer model, a fast publisher can overwhelm a slow subscriber, leading to buffer overflows or `OutOfMemoryError`.
* **Reactive Solution:** The Subscriber *requests* a specific number of items via `Subscription.request(n)`. The Publisher only pushes what has been requested.
III. Concrete Example: `SubmissionPublisher`
Java provides a built-in `SubmissionPublisher` that implements the `Flow.Publisher` interface for easy in-process event bus creation.
```java
public class MetricService {
private final SubmissionPublisher<Double> publisher = new SubmissionPublisher<>();
public void start() {
publisher.subscribe(new Flow.Subscriber<>() {
private Flow.Subscription subscription;
@Override
public void onSubscribe(Flow.Subscription sub) {
this.subscription = sub;
subscription.request(1); // Backpressure: request only 1
}
@Override
public void onNext(Double item) {
process(item); // Simulate work
subscription.request(1); // Ask for next only after processing
}
@Override public void onError(Throwable t) { t.printStackTrace(); }
@Override public void onComplete() { System.out.println("Done"); }
});
}
public void submit(double val) { publisher.submit(val); }
}
```
IV. Technical Integrity and Anti-patterns
1. **Memory Leaks:** Always unsubscribe. In long-lived subjects, holding references to short-lived observers prevents GC. Use `WeakReference` if manual unsubscription isn't guaranteed.
2. **Thread Safety:** Use a `CopyOnWriteArrayList` for the observer list to allow concurrent modification (subscription/unsubscription) during a broadcast.
3. **Order Sensitivity:** Never design observers that depend on execution order. Notifications should be independent.
4. **Deep Chaining:** Chaining observers (A notifies B, B notifies C) creates "Event Hell." Use a central event bus or orchestrator for complex flows.
---
**See Also:**
- [Reactive Programming](ReactiveProgramming) — Advanced stream composition (RxJS/Project Reactor).
- [Event Driven Architecture](EventDrivenArchitecture) — Scaling the observer pattern across services.
- [Java Concurrency](JavaConcurrencyPatterns) — Threads and executors used by publishers.