Learn About Observer Design Pattern

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

  • Subject: This interface will declare the operations for adding and removing observers and send notifications to the registered observers when the subject state changes. We can add any number of observers. In our example, it is the ISubject interface.
  • ConcreteSubject: This will be a concrete class, and this class should implement the Subject interface. This class maintains its own state, and when the state is changed, it will send notifications to all observers by calling the observer’s update method. In our example, it is the Subject class.
  • Observer: This will be an interface that defines an updating interface for objects that should be notified when the subject state changes. In our example, it is the IObserver interface.
  • ConcreteObserver: This will be a class that implements the Observer interface. This interface provides one method to determine what changes have occurred with the subject. The Subject will call this Observer method to send the notification. In our example, it is the Observer class.

Advantages of Observer design pattern

  • Loose Coupling: The subject and observers are loosely coupled. The subject knows nothing about the observers except that they implement the observer interface.
  • Dynamic Relationships: You can add and remove observers dynamically at runtime.
  • Broadcast Communication: The subject broadcasts notifications to all interested observers without knowing their details.

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

  • Stock (the subject) represents an individual stock on the market. It maintains a list of traders (observers) interested in price changes for that stock.
  • Trader (the observer) represents an individual or entity interested in receiving updates for specific stocks.
  • All registered traders are notified when the stock’s price changes (e.g., IRFCStock.Price = 176.50).