The Observer pattern is a behavioral design pattern that defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
Let's implement a stock market system where multiple displays need to show updated stock prices when they change.
public interface IObserver { void Update(string stockSymbol, decimal stockPrice); }
public interface ISubject { void RegisterObserver(IObserver observer); void RemoveObserver(IObserver observer); void NotifyObservers(); } public class StockMarket : ISubject { private List<IObserver> _observers = new List<IObserver>(); private Dictionary<string, decimal> _stockPrices = new Dictionary<string, decimal>(); public void RegisterObserver(IObserver observer) { _observers.Add(observer); } public void RemoveObserver(IObserver observer) { _observers.Remove(observer); } public void NotifyObservers() { foreach (var stock in _stockPrices) { foreach (var observer in _observers) { observer.Update(stock.Key, stock.Value); } } } public void UpdateStockPrice(string stockSymbol, decimal newPrice) { if (_stockPrices.ContainsKey(stockSymbol)) { _stockPrices[stockSymbol] = newPrice; } else { _stockPrices.Add(stockSymbol, newPrice); } NotifyObservers(); } }
public class StockPriceDisplay : IObserver { public void Update(string stockSymbol, decimal stockPrice) { Console.WriteLine($"Stock Display: {stockSymbol} is now ${stockPrice}"); } } public class MobileStockApp : IObserver { public void Update(string stockSymbol, decimal stockPrice) { Console.WriteLine($"Mobile App Notification: {stockSymbol} price update - ${stockPrice}"); } } public class StockAnalyzer : IObserver { private Dictionary<string, decimal> _priceHistory = new Dictionary<string, decimal>(); public void Update(string stockSymbol, decimal stockPrice) { if (!_priceHistory.ContainsKey(stockSymbol)) { _priceHistory.Add(stockSymbol, stockPrice); Console.WriteLine($"Analyzer: First price recorded for {stockSymbol}"); } else { decimal change = stockPrice - _priceHistory[stockSymbol]; Console.WriteLine($"Analyzer: {stockSymbol} changed by {change:C} ({change/_priceHistory[stockSymbol]:P})"); _priceHistory[stockSymbol] = stockPrice; } } }
class Program { static void Main(string[] args) { // Create the subject (observable) StockMarket market = new StockMarket(); // Create observers StockPriceDisplay display = new StockPriceDisplay(); MobileStockApp mobileApp = new MobileStockApp(); StockAnalyzer analyzer = new StockAnalyzer(); // Register observers market.RegisterObserver(display); market.RegisterObserver(mobileApp); market.RegisterObserver(analyzer); // Update stock prices (this will notify all observers) market.UpdateStockPrice("AAPL", 182.63m); market.UpdateStockPrice("MSFT", 403.78m); Console.WriteLine("\nRemoving mobile app observer...\n"); market.RemoveObserver(mobileApp); market.UpdateStockPrice("AAPL", 183.25m); market.UpdateStockPrice("GOOGL", 173.45m); } }
The .NET framework uses the Observer pattern in:
In C#, you can also implement the Observer pattern using events:
public class StockMarket { public event Action<string, decimal> StockPriceChanged; public void UpdateStockPrice(string stockSymbol, decimal newPrice) { StockPriceChanged?.Invoke(stockSymbol, newPrice); } } // Usage: StockMarket market = new StockMarket(); market.StockPriceChanged += (symbol, price) => Console.WriteLine($"Stock {symbol} is now {price}");
This event-based approach is more idiomatic in C# for many observer scenarios.
Comments - Beta - WIP