Observer Pattern in C#


Sharing a good note on the Observer Pattern!!

The Observer Pattern is a mechanism for drastically reducing coupling between interacting classes.

Intent: It's intent is to define a one-to-many dependency between objects so that when one object changes state all its dependents are notified and updated automatically.

Motivation & Applicability: The Observer pattern encapsulate the core components in a subject abstraction, and the variable components in an Observer hierarchy. The Observer pattern is used when the change of a state in one object must be reflected in another object without keeping the objects tightly coupled. Also when the framework we are writing needs to be enhanced in the future with new observers with minimal changes.

Observer pattern can be applied in any of the following situations:
  • It can be applied when the abstraction has two aspects with one dependent on the other. Encapsulating these aspects in separate objects will increase the chance to reuse them independently.
  • Use it when the subject object does not know exactly how many observer objects it has.
  • Or use it when the subject object should be able to notify its observer objects without knowing who these objects are.
The classes and/or objects participating in this pattern are: 
  • Subject  (Stock) - Knows its observers. Any number of Observer objects may observe a subject. Provides an interface for attaching and detaching Observer objects.
  • ConcreteSubject  (IBM) - Stores state of interest to ConcreteObserver.Sends a notification to its observers when its state changes
  • Observer  (IInvestor) - Defines an updating interface for objects that should be notified of changes in a subject.
  • ConcreteObserver  (Investor) - Maintains a reference to a ConcreteSubject object. Stores state that should stay consistent with the subject's. Implements the Observer updating interface to keep its state consistent with the subject's.
Sample code

/* Example for Observer pattern in which registered investors   are notified every time a stock changes value. */

class MainApp
{
    static void Main()
    {
        // Create IBM stock and attach investors
        IBM ibm = new IBM("IBM", 120.00);
        ibm.Attach(new Investor("Sorros"));
        ibm.Attach(new Investor("Berkshire"));

        // Fluctuating prices will notify investors
        ibm.Price = 120.10;
        ibm.Price = 121.00;

        // Wait for user
        Console.ReadKey();
    }
}

// The 'ConcreteObserver' class
class Investor : IInvestor
{
    private string _name;
    private Stock _stock;
    public Investor(string name)
    {
        this._name = name;
    }
    public void Update(Stock stock)
    {
        Console.WriteLine("Notified {0} of {1}'s " +
          "change to {2:C}", _name, stock.Symbol, stock.Price);
    }
    public Stock Stock
    {
        get { return _stock; }
        set { _stock = value; }
    }
}

// The 'ConcreteSubject' class
class IBM : Stock
{
   public IBM(string symbol, double price): base(symbol, price){}
}

// The 'Observer' interface
interface IInvestor
{
    void Update(Stock stock);
}

// The 'Subject' abstract class
abstract class Stock
{
    private string _symbol;
    private double _price;
    private List<IInvestor> _investors = new List<IInvestor>();
    // Constructor
    public Stock(string symbol, double price)
    {
        this._symbol = symbol;
        this._price = price;
    }
    public void Attach(IInvestor investor)
    {
        _investors.Add(investor);
    }
    public void Detach(IInvestor investor)
    {
        _investors.Remove(investor);
    }
    public void Notify()
    {
        foreach (IInvestor investor in _investors)
        {
            investor.Update(this);
        }
        Console.WriteLine("");
    }
    // Gets or sets the price
    public double Price
    {
        get { return _price; }
        set
        {
            if (_price != value)
            {
               _price = value;
                Notify();
            }
        }
    }
    // Gets the symbol
    public string Symbol
    {
        get { return _symbol; }
    }
}

/* Output :

Notified Sorros of IBM's change to $120.10
Notified Berkshire of IBM's change to $120.10
Notified Sorros of IBM's change to $121.00
Notified Berkshire of IBM's change to $121.00 */