In modern applications, components must react quickly to changes: an interface must update when data changes, a service must send notifications, and a distributed system must respond to events without tight dependencies. Observer Pattern solves exactly this problem, allowing reactive notifications between objects in an elegant and extensible way.
Observer is one of the most used behavioral patterns and is the foundation of event-driven architectures, being present everywhere in .NET: from event and delegate, up to IObservable / IObserver.
What problem does the Observer Pattern solve?
The problem arises when:
-
an object (Subject) changes its state;
-
multiple objects (Observers) need to be informed automatically;
-
we do not want the Subject to know the concrete implementation of the observers.
A naive solution would be to call methods directly on other classes, but this quickly leads to tight coupling and hard-to-maintain code. The Observer Pattern introduces a standard mechanism through which observers can subscribe and unsubscribe dynamically.
Pattern structure
The Observer Pattern involves four main elements:
-
Subject – the observed object, which emits notifications;
-
Observer – the objects interested in changes;
-
Attach / Detach – subscription mechanisms;
-
Notify – the method through which observers are informed.
Simple example using event in C#
In .NET, the most common implementation of the Observer Pattern is done using event and delegate.
Subject
public class OrderService
{
public event Action<string>? OrderStatusChanged;
public void ProcessOrder()
{
// processing logic
UpdateStatus("Order processed");
}
private void UpdateStatus(string status)
{
OrderStatusChanged?.Invoke(status);
}
}
Observers
public class EmailNotifier
{
public void OnOrderStatusChanged(string status)
{
Console.WriteLine($"Email sent: {status}");
}
}
public class AuditLogger
{
public void OnOrderStatusChanged(string status)
{
Console.WriteLine($"Audit log: {status}");
}
}
Usage
var orderService = new OrderService();
var emailNotifier = new EmailNotifier();
var auditLogger = new AuditLogger();
orderService.OrderStatusChanged += emailNotifier.OnOrderStatusChanged;
orderService.OrderStatusChanged += auditLogger.OnOrderStatusChanged;
orderService.ProcessOrder();
🔹 Major advantage: OrderService knows nothing about what the observers do.
Observer Pattern with IObservable / IObserver
For more advanced scenarios, .NET offers the interfaces IObservable<T> and IObserver<T>.
Subject
public class TemperatureSensor : IObservable<int>
{
private readonly List<IObserver<int>> _observers = new();
public IDisposable Subscribe(IObserver<int> observer)
{
_observers.Add(observer);
return new Unsubscriber(_observers, observer);
}
public void SetTemperature(int value)
{
foreach (var observer in _observers)
observer.OnNext(value);
}
}
Observer
public class TemperatureDisplay : IObserver<int>
{
public void OnNext(int value)
{
Console.WriteLine($"Temperature updated: {value}");
}
public void OnCompleted() { }
public void OnError(Exception error) { }
}
This approach is very suitable for continuous data streams and reactive systems.
When to use the Observer Pattern?
The Observer Pattern is ideal when:
-
you need automatic notifications;
-
you build reactive UIs;
-
you implement event sourcing or domain events;
-
you want extensibility without modifying existing code.
Real examples:
-
events in ASP.NET (
EventHandler); -
notifications in desktop applications;
-
internal messaging between components;
-
integration with external systems.
Advantages and disadvantages
✅ Advantages
-
decoupling between objects;
-
high extensibility;
-
excellent native support in .NET.
⚠️ Disadvantages
-
harder debugging;
-
unpredictable order of notifications;
-
risk of memory leaks if
unsubscribeis not done.
Conclusion
The Observer Pattern is one of the most powerful and useful techniques in object-oriented programming. In .NET, its implementation is natural and elegant, either through event or through IObservable.
If you work with reactive applications, UIs, or modern architectures, this pattern is not optional — it is essential.