The Observer pattern seeks to allow objects to notify their observers when their internal state changes.
This means that a single object will need to be aware of the objects that observe it, and need to be able to communicate to those observers that the subject's state changed. Further, the observers should be notified automatically.
NOTE: This post is part of a series demonstrating software design patterns using C# and .NET. The patterns are taken from the book Design Patterns by the Gang of Four. Check out the other posts in the series!
The Rundown
- Type: Behavioral
- Useful? 4/5 (Very)
- Good For: Notifying observer objects that a particular subject's state changed.
- Example Code: On GitHub
The Participants
- The Subject knows its Observers and provides an interface for attaching or detaching any number of Observer objects.
- The ConcreteSubjects store the states of interest to the Observers and are responsible for sending a notification when the ConcreteSubject's state changes.
- The Observer defines an updating interface for objects that should be notified of changes in a Subject.
- The ConcreteObserver objects maintain a reference to a ConcreteSubject and implement the Observer updating interface to keep its state consistent with that of the Subject's.
A Delicious Example
To model this, let's imagine that we need a system to model the fluctuating prices of vegetables at our local market (e.g. a stock market, but for vegetables).
On some days, the vegetables will be more expensive than on other days, due to factors like the size of the harvest or the size of the vegetables themselves. Further, we need to allow restaurants to watch the prices and place an order when the price for a particular vegetable falls below a specified threshold, which is different for each restaurant.
First, let's define our Subject participant abstract class Veggies
, which needs to implement methods by which it can attach or detach observers and keeps track of a certain Veggie's current price:
/// <summary>
/// The Subject abstract class
/// </summary>
abstract class Veggies
{
private double _pricePerPound;
private List<IRestaurant> _restaurants = new List<IRestaurant>();
public Veggies(double pricePerPound)
{
_pricePerPound = pricePerPound;
}
public void Attach(IRestaurant restaurant)
{
_restaurants.Add(restaurant);
}
public void Detach(IRestaurant restaurant)
{
_restaurants.Remove(restaurant);
}
public void Notify()
{
foreach (IRestaurant restaurant in _restaurants)
{
restaurant.Update(this);
}
Console.WriteLine("");
}
public double PricePerPound
{
get { return _pricePerPound; }
set
{
if (_pricePerPound != value)
{
_pricePerPound = value;
Notify(); //Automatically notify our observers of price changes
}
}
}
}
We also need a ConcreteSubject which represents the price of a specific vegetable; in this case, carrots.
/// <summary>
/// The ConcreteSubject class
/// </summary>
class Carrots : Veggies
{
public Carrots(double price) : base(price) { }
}
Now we can define our Observer participant. Remember that restaurants want to observe the vegetable prices, so our Observer will naturally be an interface IRestaurant
, and this interface must define a method by which its implementors can be updated:
/// <summary>
/// The Observer interface
/// </summary>
interface IRestaurant
{
void Update(Veggies veggies);
}
Finally we need our ConcreteObserver class, which represent specific restaurants. This class must implement the Update()
from IRestaurant
:
/// <summary>
/// The ConcreteObserver class
/// </summary>
class Restaurant : IRestaurant
{
private string _name;
private Veggies _veggie;
private double _purchaseThreshold;
public Restaurant(string name, double purchaseThreshold)
{
_name = name;
_purchaseThreshold = purchaseThreshold;
}
public void Update(Veggies veggie)
{
Console.WriteLine("Notified {0} of {1}'s "
+ " price change to {2:C} per pound.",
_name, veggie.GetType().Name, veggie.PricePerPound);
if(veggie.PricePerPound < _purchaseThreshold)
{
Console.WriteLine(_name + " wants to buy some "
+ veggie.GetType().Name + "!");
}
}
}
Note that the Restaurants will want to buy veggies if the price dips below a certain threshold amount, which differs per restaurant.
To put this all together, in our Main()
method we can define a few restaurants that want to observe the price of carrots, then fluctuate that price:
static void Main(string[] args)
{
// Create price watch for Carrots
// and attach restaurants that buy carrots from suppliers.
Carrots carrots = new Carrots(0.82);
carrots.Attach(new Restaurant("Mackay's", 0.77));
carrots.Attach(new Restaurant("Johnny's Sports Bar", 0.74));
carrots.Attach(new Restaurant("Salad Kingdom", 0.75));
// Fluctuating carrot prices will notify subscribing restaurants.
carrots.PricePerPound = 0.79;
carrots.PricePerPound = 0.76;
carrots.PricePerPound = 0.74;
carrots.PricePerPound = 0.81;
Console.ReadKey();
}
If we run the app, we see that as the price changes, the restaurants get notified, and if the price drops below each restaurant's threshold, that restaurant then wants to place an order. Here's a sample output:
As we can see, the subject object (Carrots
) automatically notifies the observer restaurants of its own price changes, which can then decide what to do with that information (e.g. place an order for more carrots).
Will I Ever Use This Pattern?
Most likely. This is a fairly common pattern, and the ability to automatically notify dependent objects of a subject's state change is highly desirable in my opinion. However, as with all software design patterns, be sure you aren't shoehorning the Observer pattern into a solution where it doesn't fit.
Summary
The Observer pattern seeks to allow Observer objects to automatically receive notifications (and possibly change their own state) when a Subject class changes its state. In short, should the Subject change, the Observers will be notified about said change.
Happy Coding!