Introduction
What is an Observer Design Pattern? The Observer Design Pattern is a Behavioral Pattern used to notify all the objects that are registered/attached/added to the same type of observer.
Why use the Observer Design Pattern?
When there is a requirement of "Single object change in its state/behavior/Value needs to notify all other objects which are observing the same object".
Basically, a single object should notify multiple objects of the same observer type. It's the relation of one object to many others.
Players in this pattern
Subject: Provides a contract to add/remove observers
ConcreteSubject: Implements a contract defined by Subject
Observer: Provides a contract for updating objects when there is a change in the subject state/behavior/Value
ConcreteObserver: Implements a contract defined by Observer
Example:
Problem definition: Design a software solution for a wholesale pen seller. This wholesale pen seller will sell pens to shops. When there is a change in pen price per market demand, it should automatically notify the change in pen price to all shops.
The players are:
Subject – IPen
ConcreteSubject – Pen
Observer – IShop
ConcreteObserver – Shop
Below is the IPen Interface which will define contact for ConcreteSubjects
- namespace Observer.Design.Patterns
- {
- public interface IPen
- {
- void Add(IShop shop);
- void Remove(IShop shop);
- void Notify();
- }
- }
Below is the Pen class which implements IPen Interafce to add/remove Observers and call Notify, which will invoke when there is a change in pen price
- using System;
- using System.Collections.Generic;
-
- namespace Observer.Design.Patterns
- {
- public class Pen : IPen
- {
- public Pen(double penPrize)
- {
- _penPrize = penPrize;
- }
- public List<IShop> shops = new List<IShop>();
-
- public void Add(IShop shop) => shops.Add(shop);
-
- public void Remove(IShop shop) => shops.Remove(shop);
-
- public void Notify()
- {
- foreach (IShop shop in shops)
- shop.Update(this);
-
- Console.WriteLine("----------------------------------------------------------");
- }
-
- private double _penPrize;
-
- public double PenPrize
- {
- get => _penPrize;
- set
- {
- if (_penPrize != value)
- {
- _penPrize = value;
- Notify();
- }
- }
- }
-
- }
- }
Below is the IShop (Observer) interface which defines a contract for ConcreteObserver (i.e. in our case Shop)
- namespace Observer.Design.Patterns
- {
- public interface IShop
- {
- void Update(Pen pen);
- }
- }
Below is the Shop class which implements IShop to update the Subject object
- using System;
-
- namespace Observer.Design.Patterns
- {
- public class Shop : IShop
- {
- private string _shopName;
-
- public Shop(string shopName) => _shopName = shopName;
-
- public void Update(Pen pen)
- {
- Console.WriteLine($" pen prize changed to { pen.PenPrize} in {_shopName}");
- }
- }
- }
We will see the execution and the result now
- using System;
-
- namespace Observer.Design.Patterns
- {
- public class Program
- {
- public static void Main()
- {
- Pen pen = new Pen(10);
- pen.Add(new Shop("Shop1"));
- pen.Add(new Shop("Shop2"));
- pen.Add(new Shop("Shop3"));
-
- pen.PenPrize = 20;
- pen.PenPrize = 30;
- pen.PenPrize = 40;
-
- Console.ReadLine();
- }
- }
- }
Below is the result screenshot. In the result, we can see that change in pen price value has notified all shops
Summary
In this article, we have learned through code example what Observer design pattern is, as well as why and where to use it.