Observer Design Pattern in C# with Example

1. Definition

The Observer Design Pattern defines a one-to-many dependency between objects so that when one object changes its state, all its dependents are notified and updated automatically. This pattern is particularly useful for event-driven systems.

2. Problem Statement

Imagine an application where multiple components need to react to changes in a shared resource or entity. Without an efficient mechanism to notify interested parties, you might end up with tightly coupled systems or frequently polling the resource to detect changes.

3. Solution

The Observer pattern suggests that you add a subscription mechanism to the "subject" object, so other objects can register and receive updates. The main components are:

1. Subject - Maintains a list of observers and notifies them of state changes.

2. Observer - Provides an updating interface for objects that need to be informed of changes in the subject.

4. Real-World Use Cases

1. News publishers and subscribers.

2. Event management systems.

3. UI libraries where a change in one component reflects in multiple UI elements.

5. Implementation Steps

1. Define the Observer and Subject interfaces.

2. Implement concrete classes for the Observer and Subject interfaces.

3. The Subject notifies all registered Observers when there's a change in its state.

6. Implementation in C#

// Observer interface
public interface IObserver
{
    void Update(string message);
}

// Subject interface
public interface ISubject
{
    void RegisterObserver(IObserver observer);
    void RemoveObserver(IObserver observer);
    void NotifyObservers();
}

public class ConcreteSubject : ISubject
{
    private List<IObserver> _observers = new List<IObserver>();
    private string _message;

    public void RegisterObserver(IObserver observer)
    {
        _observers.Add(observer);
    }

    public void RemoveObserver(IObserver observer)
    {
        _observers.Remove(observer);
    }

    public void NotifyObservers()
    {
        foreach (var observer in _observers)
        {
            observer.Update(_message);
        }
    }

    public void SetState(string message)
    {
        _message = message;
        NotifyObservers();
    }
}

public class ConcreteObserver : IObserver
{
    private string _name;

    public ConcreteObserver(string name)
    {
        _name = name;
    }

    public void Update(string message)
    {
        Console.WriteLine($"Observer {_name} received message: {message}");
    }
}

public class Program
{
    public static void Main()
    {
        ConcreteSubject subject = new ConcreteSubject();

        ConcreteObserver observer1 = new ConcreteObserver("A");
        ConcreteObserver observer2 = new ConcreteObserver("B");

        subject.RegisterObserver(observer1);
        subject.RegisterObserver(observer2);

        subject.SetState("Hello, Observers!");
    }
}

Output:

Observer A received message: Hello, Observers!
Observer B received message: Hello, Observers!

Explanation:

In this implementation, the ConcreteSubject maintains a list of observers. 

When the SetState method is called, it updates its message and notifies all registered observers. 

The observers (in this case, ConcreteObserver A and B) then process the message and print it.

7. When to use?

1. Use the Observer pattern when changes to the state of one object must be reflected in another without making the classes tightly coupled.

2. When an abstraction has multiple aspects with independent functionality, separate them out and let dependent components observe them.


Comments