Observer Design Pattern: Create a Real-Time Stock Market Notification System in C#
What is the Observer Design Pattern? The Observer Design Pattern is a behavioral design pattern that allows objects to be notified of changes to other objects’ states. It establishes a one-to-many dependency between objects, where the subject (the object being observed) maintains a list of observers (objects that are interested in its state) and notifies them of any changes.
Why use the Observer Design Pattern? The Observer pattern is useful when you need to notify multiple objects of changes to a single object’s state without tightly coupling them. It promotes loose coupling between objects, making it easier to maintain and extend the system. It is commonly used in event-driven systems, such as GUI frameworks, real-time monitoring systems, and notification systems.
Key components of the Observer Design Pattern:
- Subject: The object being observed, which maintains a list of observers and notifies them of changes to its state.
- Observer: The object that is interested in the subject’s state and receives notifications when the subject’s state changes.
- Concrete Subject: A specific implementation of the subject that contains the state to be observed.
- Concrete Observer: A specific implementation of the observer that receives notifications from the subject.
- Attach / Registration: The process of adding an observer to the subject’s list of observers.
- Detach / Deregistration: The process of removing an observer from the subject’s list of observers.
- Notify: The process of notifying all observers of changes to the subject’s state.
UML Class Diagram of the Observer Design Pattern:
How to implement the Observer Design Pattern in C#?
namespace ObserverPattern
{
// IObserver interface is used to update all the observers
public interface IObserver
{
// Update method is called when the subject changes
void Update(string state);
}
// Interface for the subject
// It has methods to attach, detach and notify the observers
public interface ISubject
{
void Attach(IObserver observer);
void Detach(IObserver observer);
void Notify();
}
// Concreate Subject class
public class ConcreateSubject : ISubject
{
// List of observers attached to the subject
private List<IObserver> _observers = new List<IObserver>();
private string _subjectState;
public string SubjectState
{
get => _subjectState;
// When the state changes of subject, notify all the observers
set
{
_subjectState = value;
// Call Notify method to notify all the observers
Notify();
}
}
public void Attach(IObserver observer)
{
_observers.Add(observer);
}
public void Detach(IObserver observer)
{
_observers.Remove(observer);
}
// Notify all the observers
public void Notify()
{
// Call Update method of all the observers attached to the subject
foreach (var observer in _observers)
{
observer.Update(_subjectState);
}
}
}
// Concrete Observer class
public class ConcreateObserver : IObserver
{
// Used to set the name of the observer
private string _observerName;
public ConcreateObserver(string name)
{
_observerName = name;
}
public void Update(string state)
{
Console.WriteLine($"{_observerName} received update: State changed to {state}");
}
}
internal class Program
{
static void Main(string[] args)
{
// Create a subject
ConcreateSubject subject = new ConcreateSubject();
// Create observers
IObserver observer1 = new ConcreateObserver("Observer-1");
IObserver observer2 = new ConcreateObserver("Observer-2");
IObserver observer3 = new ConcreateObserver("Observer-3");
// Attach the observers to the subject
subject.Attach(observer1);
subject.Attach(observer2);
subject.Attach(observer3);
// Change the state of the subject
Console.WriteLine("Changing state to State 1");
subject.SubjectState = "State-1";
Console.WriteLine("Changing state to State 2");
subject.SubjectState = "State-2";
// Detach observer 1
subject.Detach(observer1);
Console.WriteLine("Changing state to State 3");
subject.SubjectState = "State-3";
Console.ReadLine();
}
}
}
Output
Changing state to State 1
Observer-1 received update: State changed to State-1
Observer-2 received update: State changed to State-1
Observer-3 received update: State changed to State-1
Changing state to State 2
Observer-1 received update: State changed to State-2
Observer-2 received update: State changed to State-2
Observer-3 received update: State changed to State-2
Changing state to State 3
Observer-2 received update: State changed to State-3
Observer-3 received update: State changed to State-3
Use Case: Real-Time Stock Market Notification System Imagine a stock market application where traders are notified every time the price of a stock changes. In this scenario, the stock price is the subject, and the traders are the observers. When the price of a stock changes, all the traders who are interested in that stock should be notified of the change. Let’s implement this use case using the Observer Design Pattern in C#.
UML Class Diagram of the Real-Time Stock Market Notification System:
Implementation of the Real-Time Stock Market Notification System in C#
Step 1: Define the Observer Interface The observer interface provides a common contract for all observers to implement.
public interface IObserver
{
void Update(string symbol, decimal price);
}
Step 2: Define the Subject Interface
The subject interface defines the methods to register, unregister, and notify observers. Here we define it as IStock
.
public interface IStock
{
void Register(IObserver observer);
void Unregister(IObserver observer);
void Notify();
}
Step 3: Implement the Stock Class
The Stock
class is a concrete subject that maintains the stock price and notifies the observers when the price changes.
public class Stock : IStock
{
private string _symbol;
private decimal _price;
private List<IObserver> _observers = new List<IObserver>();
public Stock(string symbol, decimal price)
{
_symbol = symbol;
_price = price;
}
public void Register(IObserver observer)
{
_observers.Add(observer);
}
public void Unregister(IObserver observer)
{
_observers.Remove(observer);
}
// If the price changes, notify the observers (traders)
// Here we are calling the Notify method
public void UpdatePrice(decimal price)
{
_price = price;
Notify();
}
public void Notify()
{
foreach (var observer in _observers)
{
observer.Update(_symbol, _price);
}
}
}
Step 4: Implement the Trader Class
The Trader
class is a concrete observer that receives notifications when the stock price changes.
public class Trader : IObserver
{
// The observer's name
private string _name;
public Trader(string name)
{
_name = name;
}
public void Update(string symbol, decimal price)
{
Console.WriteLine($"Notified {_name} of {symbol}'s price change to {price}");
}
}
Step 5: Implement the Main Program In the main program, we create a stock object and register traders as observers. When the stock price changes, the traders are notified.
public class Program
{
static void Main(string[] args)
{
// Create a stock
Stock stock = new Stock("MSFT", 100.00m);
// Create some traders
Trader joe = new Trader("Joe");
Trader sally = new Trader("Sally");
// Register/subscribe the traders
Console.WriteLine("Registering Joe and Sally");
stock.Register(joe);
stock.Register(sally);
Console.WriteLine("Changing stock price to 105.00m");
// Change the stock price
stock.UpdatePrice(105.00m);
// Change the stock price
Console.WriteLine("Changing stock price to 110.00m");
stock.UpdatePrice(110.00m);
// Unregister/unsubscribe a trader
Console.WriteLine("Unregistering Joe");
stock.Unregister(joe);
Console.WriteLine("Changing stock price to 115.00m");
// Change the stock price
stock.UpdatePrice(115.00m);
}
}
Now, when you run the program, you should see the following output:
Output
Registering Joe and Sally
Changing stock price to 105.00m
Notified Joe of MSFT's price change to 105.00
Notified Sally of MSFT's price change to 105.00
Changing stock price to 110.00m
Notified Joe of MSFT's price change to 110.00
Notified Sally of MSFT's price change to 110.00
Unregistering Joe
Changing stock price to 115.00m
Notified Sally of MSFT's price change to 115.00
Happy coding!