Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Design Patterns In C# - Observer Design Pattern

DZone's Guide to

Design Patterns In C# - Observer Design Pattern

·
Free Resource
So far this week we've taken a look at the following Design Patterns:
  1. Singleton
  2. Prototype
  3. Factory


All those are Creational Patterns, design patterns that deal with object creation in an attempt to create them in a suitable manner for the given situation. Today we'll be looking at the Observable Pattern, which is a Behavioral Pattern, meaning it deals with communication between objects and how they are dealt with.

What Is It?
The Observer Pattern is used to implement communication between objects. Since the state of an object (we'll call it subject) is constantly changing, the Observer pattern allows us to define a one to many dependency between other objects (we'll call them observers) to communicate with them letting all the observers know that a subject has changed its state, such as a blogging system where all subscribers are notified when a new blog entry is made. This allows us to notify all observers with a single mechanism.

The Observer Pattern is set up in the publisher/subscriber pattern, and is excellent for implementing distributed event handling systems and such. This pattern also goes a long way towards removing the coupling between the subject & observer with the use of the IObserver interface.

How to Implement?
At one point implementing the Observer pattern was rather cumbersome & challenging but definitely was worth the effort because of all the advantages if offered. Then along comes the .Net Framework, which gave us Delegates & Events and a far easier way to implement this pattern. Delegates & Events are an implementation of the Observer pattern in the same way a for each loop is an implementation of the Iterator pattern.

Using Delegates & Events gives us a clean, concise way of implementing the Observer pattern, and in a lot of ways is far more efficient than the traditional manner. The traditional way of implementing the Observer way using interfaces went a long way towards removing coupling between classes, while using delegates & events completely removes the coupling and allows them to act independently of each other.

In our example we will be creating a stock market application, which will use the Observer Pattern to notify Observers (in our case investors) when a stock has reached a certain price so they can purchase the stock. The classes we will have in play are:

  • Stock.cs
  • StockMarket.cs
  • IObserve.cs
  • Investor.cs (Implements our IObserve interface)
  • StockDetailsEventArgs.cs

Stock.cs will contain 3 properties

  • Name
  • OpeningPrice
  • ClosingPrice

And will be the base for the application, here’s how our stock class looks like:

using System;

namespace DZoneArticles.ObserverDesignPattern
{
public class Stock
{
public string Name { get; set; }
public double OpeningPrice { get; set; }
public double ClosingPrice { get; set; }

public Stock(string name, double open, double close)
{
this.Name = name;
this.OpeningPrice = open;
this.ClosingPrice = close;
}
}
}

StockMarket.cs will be where we will implement events & delegated for the communication between it and Investor.cs. We will simulate the price of a stock dropping, and use an event (PriceChangedEvent) to let the observers (Investor.cs) know when it’s reached a certain price so they can buy. So here’s the content of StockMarket.cs:

using System;

namespace DZoneArticles.ObserverDesignPattern
{
public class StockMarket
{
public delegate void PriceChangeEventHandler(object sender, StockDetailsEventArgs e);
public event PriceChangeEventHandler PriceChangedEvent;
private Stock stock { get; set; }

public StockMarket(Stock s)
{
this.stock = s;
}

/// <summary>
/// method used to trigger the start of trading
/// </summary>
public void Start()
{
//make sure a stock is provided, otherwise throw an error
if (stock == null)
throw new ArgumentException("A stock must be provided before you can start trading");
else
{
//get the current price of the stock being watched
double currentPrice = this.stock.OpeningPrice;

//simulate the price of the stock falling in the market
//our notification will let the investor know when
//it's time to buy
while (currentPrice > stock.ClosingPrice)
{
//simulate price dropping by $1 at a time
currentPrice -= 1.00;

//trigger our event so the Observer will know
OnStockPriceChanged(currentPrice);

//simply to make it drop lower (for demonstrating this example)
System.Threading.Thread.Sleep(3000);
}
}
}

/// <summary>
/// our price change event
/// </summary>
/// <param name="current">current price of the stock when the event is triggered</param>
protected void OnStockPriceChanged(double current)
{
if (PriceChangedEvent != null)
{
//create an instance of our event args
StockDetailsEventArgs e = new StockDetailsEventArgs();

//set the current price of the stock
e.StockCurrentPrice = current;

//display the current price
Console.WriteLine(string.Format("{0}'s current stock price is now {1:C}", this.stock.Name, e.StockCurrentPrice));

//now raise the price changed event
PriceChangedEvent(this, e);
}
}

public void AttachEvent(IObserve observe)
{
this.PriceChangedEvent += new PriceChangeEventHandler(observe.StockPriceChanged);
}
}
}

You will notice a reference to StockDetailsEventArgs in there, this is our class containing our event data which contains a single property, StockCurrentPrice, which we will use for tracking the current stock price. This class inherits the System.EventArgs class:

using System;

namespace DZoneArticles.ObserverDesignPattern
{
public class StockDetailsEventArgs : EventArgs
{
public double StockCurrentPrice { get; set; }
}
}

Now for our interface IObserve.cs. This interface contains but a single method – StockPriceChanged – which accepts two parameters:

  • Object – sender
  • StockDetailsEventArgs – e

This was used for attaching our PriceChangedEventHandler delegate in our StockMarket.cs class. So here’s our interface

using System;

namespace DZoneArticles.ObserverDesignPattern
{
public interface IObserve
{
void StockPriceChanged(object sender, StockDetailsEventArgs e);
}
}

We’ve got one more part for implementing the Observer pattern in this example, and that’s Investor.cs. This class inherits IObserve.cs, and for that it implements the StockPriceChanged method of the interface. It is here where the notifications let the observer (Investor.cs) know if it’s time to buy the stock or not:

using System;

namespace DZoneArticles.ObserverDesignPattern
{
public class Investor : IObserve
{
private string Name { get; set; }
private double BuyAt { get; set; }
private Stock stock { get; set; }
private bool bought = false;

public Investor(string name, double price, Stock s)
{
this.Name = name;
this.BuyAt = price;
this.stock = s;
}

void IObserve.StockPriceChanged(object sender, StockDetailsEventArgs e)
{
//check if the current stock price is below this investors
//buy price, if it is then buy it
if (e.StockCurrentPrice <= this.BuyAt && !bought)
{
//have the investor buy the stock then mark bought as true so they dont buy it again
Console.WriteLine(string.Format("{0} is buying {1}'s stock at {2:C}", Name, this.stock.Name, e.StockCurrentPrice));
this.bought = true;
}
}
}
}

There we have what we need to implementing the Observer Design Pattern in C#, but we’re not done just yet. Now we need to show how to use what we’ve created. In this example we will create a new Stock instance (providing a name, opening price & closing price). We will then pass this stock to 2 instances of our investor (Investor.cs), along with the investors name and what price they will buy at. Finally we will attach a notification event (well 2 in this case since we create 2 observers) then start the StockMarket. Here’s how that looks

static void Main(string[] args)
{
//create a new stock isntance
Stock s = new Stock("DZone", 15.00, 9.00);

//provide our stock market instance know the stock
StockMarket market = new StockMarket(s);

//create 2 new investors, the price they'll buy at and what
//stock they're watching
IObserve Richard = new Investor("Richard", 11.00, s);
IObserve Lyndsey = new Investor("Lyndsey", 9.50, s);

//attach events to the stock market, so it can notify the investor
//this uses the Observe pattern with delegates & events
market.AttachEvent(Richard);
market.AttachEvent(Lyndsey);

//start the stock market
market.Start();

Console.ReadKey();
}

And that’s it, as you can see if you’ve used the Observer Pattern in previous languages or in the past, .Net’s events & delegates gives you a much cleaner, more concise way of implementing the Observer pattern. In my experience this design pattern is one of the most popular and powerful (not to mention useful) patterns that's available to use as developers.

And using delegates & events gives you, the developer, an efficient yet simple way to de-couple your classes (makes for more extensible code & applications) and easily handle notifications between your objects, allowing for more dynamic, efficient applications. In future articles we will be looking at even more design patterns that I feel will make you a more useful, efficient developer. Thanks for reading and I hope you found this useful & informative.

Topics:

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}