[← Design patterns index](README.md) # Observer **Category:** Behavioral | **Also known as: Event-Subscriber, Listener** | **Source:** `patterns/observer/ObserverPattern/` (+ `UpdatedOne/`) ## Problem One object's state change must reach an open-ended, changing set of others. Polling (the customer visiting the store daily) wastes the observers' effort; broadcasting to everyone regardless of interest (the store emailing all customers) wastes the publisher's and annoys the uninterested. ## Solution Give the state-holder (**Subject**/Publisher) a subscription list plus subscribe/unsubscribe methods. On a state change it walks the list, calling one common `Update()` method on each subscriber. All subscribers implement a single interface, so the publisher never knows their concrete types — new subscriber types can appear with **zero** changes to the publisher. ## Participants (GoF names → this repo) - **Subject** → `Subject` — the reusable subscription machinery. - **Observer** → `IObserver` — declares `Update()`. - **ConcreteSubject** → `Data` — holds the interesting state, calls `Notify()` on change. - **ConcreteObserver** → `BarGraph`, `PieChart` — each reacts, and pulls the new value back from the subject. ```mermaid classDiagram class IObserver { <> +Update() } class Subject { -List~IObserver~ Observers +Subscribe(IObserver) +Unsubscribe(IObserver) +Notify() } class Data { -int Value +SetData(int) +GetData() int } class BarGraph { -Data _data +Update() } class PieChart { -Data _data +Update() } Subject <|-- Data IObserver <|.. BarGraph IObserver <|.. PieChart Subject o--> IObserver : notifies - abstract coupling BarGraph --> Data : pulls value on update PieChart --> Data : pulls value on update ``` ## Code ```csharp // IObserver.cs — Observer interface public interface IObserver { void Update(); // called when the Subject updates } // Subject.cs — reusable subscription machinery, knows nothing about BarGraph or PieChart public class Subject { private readonly List _observers = new(); public void Subscribe(IObserver observer) { _observers?.Add(observer); } public void Unsubscribe(IObserver observer) { _observers?.Remove(observer); } public void Notify() { foreach (IObserver observer in _observers) observer.Update(); // abstract coupling: only ever the interface } } ``` ```csharp // Data.cs — ConcreteSubject; inherits the subscription machinery public class Data : Subject { private int _value; public Data(int value) { _value = value; } public void SetData(int value) { _value = value; Notify(); // every mutation fans out } public int GetData() { return _value; } } // BarGraph.cs — ConcreteObserver public class BarGraph : IObserver { private readonly Data _data; public BarGraph(Data data) { _data = data; } public void Update() { Console.WriteLine("Bar Graph:"); Console.WriteLine("\tGot an update notification"); Console.WriteLine("\tUpdating my graph with value " + _data.GetData()); Console.WriteLine(); } } // PieChart.cs — ConcreteObserver; same shape as BarGraph, its own message text public class PieChart : IObserver { private readonly Data _data; public PieChart(Data data) { _data = data; } public void Update() { Console.WriteLine("Pie Chart:"); Console.WriteLine("\tGot an update notification"); Console.WriteLine("\tUpdating my chart with value " + _data.GetData()); Console.WriteLine(); } } ``` This is the **pull** style: `Update()` takes no parameters, so the observer calls `_data.GetData()` to fetch what changed. The alternative **push** style would be `Update(int newValue)`. Pull keeps the interface stable as the subject's state grows; push avoids the observer needing a back-reference to the subject at all. GoF: _"The publisher can pass itself as an argument, letting subscriber fetch any required data directly."_ Subscription itself is explicit, not automatic — neither `BarGraph` nor `PieChart` calls `Subscribe` on itself. `Program.cs` constructs each observer and then wires it up separately, so the client controls exactly when an observer starts listening: ```csharp // Program.cs — two independent subscribers on one subject Data data = new Data(2); BarGraph bar = new BarGraph(data); PieChart pie = new PieChart(data); data.Subscribe(bar); data.Subscribe(pie); data.SetData(3); data.SetData(60); ``` Note `Data` never mentions `BarGraph` or `PieChart` by name. A third subscriber type could be added tomorrow without touching `Data` or `Subject` — that is the Open/Closed payoff. ## Output (verified) ``` Bar Graph: Got an update notification Updating my graph with value 3 Pie Chart: Got an update notification Updating my chart with value 3 Bar Graph: Got an update notification Updating my graph with value 60 Pie Chart: Got an update notification Updating my chart with value 60 ``` Both subscribers fire on each `SetData`, and each _pulls_ the current value. They happen to run in subscription order here because the backing store is a `List` — but GoF and Shvets both warn that **notification order is not part of the contract**; never write an observer that depends on running before or after another. ## GoF pseudocode (typed events via a dedicated event manager) ``` class EventManager is private field listeners: hash map of event types and listeners method subscribe(eventType, listener) is listeners.add(eventType, listener) method unsubscribe(eventType, listener) is listeners.remove(eventType, listener) method notify(eventType, data) is foreach (listener in listeners.of(eventType)) do listener.update(data) class Editor is private field events: EventManager // composition, not inheritance constructor Editor() is events = new EventManager() method openFile(path) is this.file = new File(path) events.notify("open", file.name) method saveFile() is file.write() events.notify("save", file.name) interface EventListener is method update(filename) class LoggingListener implements EventListener is method update(filename) is log.write(...) class EmailAlertsListener implements EventListener is method update(filename) is system.email(...) editor.events.subscribe("open", logger) editor.events.subscribe("save", emailAlerts) ``` Two upgrades over the repo version worth knowing: **event types**, so subscribers pick which events they want; and **composition over inheritance** — `Editor` _has an_ `EventManager` rather than _being a_ `Subject`. The repo uses inheritance (`Data : Subject`), which burns `Data`'s single base class. Shvets flags exactly this: put subscription logic in a separate object when the publisher already has a superclass. ## Applicability - An abstraction has two aspects, one dependent on the other — separate them to vary and reuse them independently. - A change to one object requires changing others, and you don't know how many. - An object should notify others **without assumptions about who they are**. - Objects should observe others only for a limited time or in specific cases — the subscription list is dynamic. ## Pros / Cons | Pros | Cons | | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | | Open/Closed — new subscribers without touching the publisher | Subscribers are notified in unspecified order | | Relationships can be made and broken at runtime | **Unexpected updates**: one innocuous change can cascade into a storm of updates | | Abstract, minimal coupling — subject and observers can live in different layers | Forgetting to unsubscribe leaks the observer (see Pitfalls) | | Supports broadcast: the sender needn't name a receiver | | ## Seen in the wild (.NET) - C# `event`/`delegate` — Observer built into the language. - `IObservable`/`IObserver` (Rx) — the GoF interface pair almost verbatim, plus the `IDisposable` subscription handle that fixes the unsubscribe problem. - `INotifyPropertyChanged` powering WPF data binding. ## Related patterns - One of the four sender/receiver connection strategies alongside **Chain of Responsibility, Command, Mediator** — Observer's distinguishing trait is _dynamic_ subscribe/unsubscribe. - **Mediator vs. Observer** — see the discussion under Mediator; the two are often combined, with the mediator acting as publisher. --- **← Prev:** [Memento](memento.md) · [All patterns](README.md) · **Next →** [State](state.md)