Introduction

In this article, we will cover the Observer Design Pattern.

What is Observer?

The Observer Design Pattern is a behavioral pattern that defines a one-to-many dependency between objects. In this pattern, when the state of one object changes, all its dependents are notified and updated automatically. This promotes loose coupling between objects, allowing for easier maintenance and scalability.

Why User Observer?

Using the Observer Pattern, you can separate the concerns of the subject (the object being observed) and the observers (the objects interested in its state changes). It provides a flexible way to design systems where changes in one object need to trigger updates in multiple other objects. Observer Pattern promotes reusable and modular code, making it easier to extend and maintain systems.

Where to Use Observer?

It proves invaluable in event-driven systems, such as message brokers or reactive programming frameworks, where events trigger actions across multiple components. By leveraging the Observer Pattern in these contexts, developers can build more responsive, modular, and maintainable software solutions.

Observer design pattern UML or Class diagram

UML or Class Diagram

Advantages of Observer design pattern

Real-World Application. The Stock market tracking system

The stock market serves as the subject, while investors act as observers. When the price of a stock changes, all subscribed investors receive immediate updates.

using System;
using System.Collections.Generic;

namespace ObserverDesignPattern
{
    public class Program
    {
        public static void Main()
        {
            Stock IRFCStock = new Stock("IRFC", 175.00);

            Trader vishal = new Trader("Vishal");
            Trader kumar = new Trader("Kumar");
            Trader sachin = new Trader("Sachin");

            // Register Traders
            IRFCStock.RegisterTrader(vishal);
            IRFCStock.RegisterTrader(kumar);
            IRFCStock.RegisterTrader(sachin);

            // Unregister
            IRFCStock.UnregisterTrader(kumar);

            // Simulate a price change
            IRFCStock.Price = 176.50;

            Console.ReadKey();
        }
    }

    // Observer Interface
    public interface ITrader
    {
        void Update(Stock stock);
    }

    // Concrete Observer: Trader
    public class Trader : ITrader
    {
        public string Name { get; set; }

        public Trader(string name)
        {
            Name = name;
        }

        public void Update(Stock stock)
        {
            Console.WriteLine($"Notifying {Name} of {stock.StockName}'s price change to {stock.Price}");
        }
    }

    // Subject Interface
    public interface IStockTicker
    {
        void RegisterTrader(ITrader trader);
        void UnregisterTrader(ITrader trader);
        void Notify();
    }

    // Concrete Subject: Stock
    public class Stock : IStockTicker
    {
        private List<ITrader> _traders = new List<ITrader>();
        public string StockName { get; private set; }
        private double _price;

        public Stock(string stockName, double price)
        {
            StockName = stockName;
            _price = price;
        }

        public double Price
        {
            get { return _price; }
            set
            {
                if (_price != value)
                {
                    _price = value;
                    Notify();
                }
            }
        }

        public void RegisterTrader(ITrader trader)
        {
            _traders.Add(trader);
        }

        public void UnregisterTrader(ITrader trader)
        {
            _traders.Remove(trader);
        }

        public void Notify()
        {
            foreach (var trader in _traders)
            {
                trader.Update(this);
            }
        }
    }
}

Output

Note

In this example