Observer is a behavioral design pattern that lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they’re observing.
Observer is used everywhere, especially in reactive environment like web UI.
- Consumers of Message Queue (Kafka)
- When a consumer subscribe to a topic, it observes the topic updates
- RxJS | Reactive Extensions Library for JavaScript
document.addEventListener()
- Frontend reactive stores
Examples
Vue
Let’s use Vue’s reactivity design as an example.
Read the docs for ref()
and reactive()
reactive()
returns a reactive proxy of the object.
So Proxy pattern can be used to implement observer-design-pattern.
Vue’s reactivity system, including ref()
, is based on the Observer pattern combined with Proxies and dependency tracking.
How Vue Uses the Observer Pattern:
- Reactive State (
ref
,reactive
):- When a reactive value is accessed (
ref.value
orreactiveObject.prop
), Vue tracks which components or effects depend on it.
- When a reactive value is accessed (
- Effect Tracking (Dependency Collection):
- When a reactive value changes, Vue notifies all dependent components/effects to re-run.
- Reactivity System:
- Vue maintains a global effect stack, so when a reactive value is read during execution, Vue knows which effect depends on it.
Sample Code
class Dep {
constructor() {
this.subscribers = new Set();
}
depend() {
if (activeEffect) {
this.subscribers.add(activeEffect);
}
}
notify() {
this.subscribers.forEach((sub) => sub());
}
}
let activeEffect = null;
function watchEffect(effect) {
activeEffect = effect;
effect();
activeEffect = null;
}
function reactive(obj) {
const depMap = new Map(); // Track dependencies for each property
return new Proxy(obj, {
get(target, key) {
if (!depMap.has(key)) {
depMap.set(key, new Dep());
}
depMap.get(key).depend();
return target[key];
},
set(target, key, value) {
target[key] = value;
if (depMap.has(key)) {
depMap.get(key).notify();
}
return true;
}
});
}
// Usage Example
const state = reactive({ count: 0 });
watchEffect(() => {
console.log(`Count changed: ${state.count}`);
});
state.count++; // Triggers reactivity