[Refactoring Guru | Observer](https://refactoring.guru/design-patterns/observer)
> **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](https://rxjs.dev/)
- `document.addEventListener()`
- Frontend reactive stores
- [Svelte Store](https://svelte.dev/docs/svelte/svelte-store)
- [Vue Pinia Store](https://pinia.vuejs.org/)
## Examples
### Vue
Let's use [[Vue]]'s reactivity design as an example.
Read the docs for [`ref()`](https://vuejs.org/api/reactivity-core.html#ref) and [`reactive()`](https://vuejs.org/api/reactivity-core.html#reactive)
`reactive()` returns a reactive proxy of the object.
So [[Kunkun/Design Patterns/Proxy|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:
1. **Reactive State (`ref`, `reactive`)**:
- When a reactive value is accessed (`ref.value` or `reactiveObject.prop`), Vue tracks which components or effects depend on it.
2. **Effect Tracking (Dependency Collection)**:
- When a reactive value changes, Vue notifies all dependent components/effects to re-run.
3. **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
```ts
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
```