The observer design pattern allows us to observe the state of an object in an application.  The best way to think about it is the publish / subscribe model.  In this pattern you have one object that is being observed called the subject, and a group of objects watching the subject called observers.  This pattern is an excellent example of loose coupling, because our classes can interact with very little knowledge of each other.

There are 3 things that a subject needs to be concerned with:

  1. Registering an Observer
  2. Removing an Observer
  3. Notifying Observers of Event

Let’s say that we are creating a video game.  In our game, we have a bunch of NPC’s (non-player characters for those non-gamers reading this) which all react differently depending on where a player is.  For the purposes of this example, let’s say we have an archer class and a swordsman class.  The archer can attack if the player is at range, and the swordsman can attack if the player is close.  How can we implement something like this? 

This is a good candidate for the observer pattern.  The subject is the player, and the player doesn’t have any idea how many NPC’s are observing him.  The beauty of it is that it doesn’t matter, since we will just be broadcasting the player position to any NPC that is interested.    This example had the potential to spin out of control on me, so I had to cut a few corners.  For example, I’m going to use 2-D coordinates because it’s easier to calculate the distance between 2 points.

Anyway, to implement the observer pattern, I first create 2 interfaces.  The first is for the subject.  The subject needs to be able to register observers, remove observers, and notify observers.  In this example, the observers will try to attack based on the position of the subject.  Here are the interfaces that I came up with for our scenario:

   1: namespace Observer
   2: {
   3:    public interface ISubject
   4:    {
   5:       void RegisterObserver(IObserver observer);
   6:       void UnregisterObserver(IObserver observer);
   7:       void NofifyObservers();
   8:    }
   9:  
  10:    public interface IObserver
  11:    {
  12:       void Attack(ref Position p);
  13:    }
  14: }

I needed to create a helper class to represent position of the character.  This class also has a method in it that will calculate the distance between 2 points.  Everything in this class should be self explanatory……although if you’re rusty on your geometry you’ll just have to trust that I calculated the distance between 2 points correctly. :)

   1: namespace Observer
   2: {
   3:    public class Position
   4:    {
   5:       private int _x;
   6:       private int _y;
   7:  
   8:       public int X
   9:       {
  10:          get { return _x; }
  11:          set { _x = value; }
  12:       }
  13:       public int Y
  14:       {
  15:          get { return _y; }
  16:          set { _y = value; }
  17:       }
  18:  
  19:       public Position()
  20:       {
  21:          _x = _y = 0;
  22:       }
  23:       public Position(int x, int y)
  24:       {
  25:          _x = x;
  26:          _y = y;
  27:       }
  28:  
  29:       // This could be done on 1 line, but I broke
  30:       // it apart for readability.
  31:       public double CalcDistance(Position pos)
  32:       {
  33:          int xsquared = (this.X - pos.X) * (this.X - pos.X);
  34:          int ysquared = (this.Y - pos.Y) * (this.Y - pos.Y);
  35:          int sum = xsquared + ysquared;
  36:          return Math.Sqrt(Convert.ToDouble(sum));
  37:       }
  38:  
  39:       public override string ToString()
  40:       {
  41:          return _x.ToString() + ", " + _y.ToString();
  42:       }
  43:    }
  44: }

Our rules are going to be simplistic, we are going to have 2 classes, Archer and Swordsman.  For our rules we will assume that an archer can only attack when between 4 and 10 units away from the player, and the swordsman must be 2 units or closer from the player to attack.   When we create the player, it must implement our ISubject interface since it is acting as the subject.  The player will also have a property holding our current position.

   1: namespace Observer
   2: {
   3:    public class Player : ISubject
   4:    {
   5:       private List<IObserver> _observers;
   6:       private Position _currentPosition;
   7:       
   8:       public Position CurrentPosition
   9:       {
  10:          get { return _currentPosition; }
  11:          set { _currentPosition = value; }
  12:       }
  13:       public List<IObserver> Observers
  14:       {
  15:          get { return _observers; }
  16:       }
  17:  
  18:       // We initialize our observer list in the constructor.
  19:       public Player()
  20:       {
  21:          _observers = new List<IObserver>();
  22:       }
  23:  
  24:       // This is the key event in our scenario.  The only thing
  25:       // that the observers care about is the position of the 
  26:       // player, so we notify them when it changes.
  27:       public void SetPosition(Position pos)
  28:       {
  29:          _currentPosition = pos;
  30:          Console.WriteLine("Player is at " + pos.ToString());
  31:          NofifyObservers();
  32:       }
  33:  
  34:       // This is our function to register a new observer.
  35:       public void RegisterObserver(IObserver observer)
  36:       {
  37:          if (!_observers.Contains(observer))
  38:             _observers.Add(observer);
  39:       }
  40:  
  41:       // This takes an observer out of the list.
  42:       public void UnregisterObserver(IObserver observer)
  43:       {
  44:          if (_observers.Contains(observer))
  45:             _observers.Remove(observer);
  46:       }
  47:  
  48:       // This does the notification.  In our case
  49:       // that means each observer will try to attack
  50:       // based on the position of the player.
  51:       public void NofifyObservers()
  52:       {
  53:          foreach (IObserver obs in _observers)
  54:             obs.Attack(ref _currentPosition);
  55:       }
  56:    }
  57: }

We have the subject set up, now we need to implement a few observers.  Notice that the RegisterObserver and UnregisterObserver methods simply add and remove observers to and from the internal list of valid observers.  These methods take an IObserver parameter, meaning that they will accept any object that implements our IObserver interface.  The NotifyObserver method simply loops through the list of observers and calls the Attack method on each one.  We know that every observer has this method because it is specified in the IObserver interface. 

As I mentioned before, I came up with 2 observers for this example, the Archer and the Swordsman.  Here are their implementations:

   1: namespace Observer
   2: {
   3:    public class Archer : IObserver
   4:    {
   5:       private string _name;
   6:       private Position _current;
   7:       public string Name
   8:       {
   9:          get { return _name; }
  10:          set { _name = value; }
  11:       }
  12:       public Position Current
  13:       {
  14:          get { return _current; }
  15:          set { _current = value; }
  16:       }
  17:  
  18:       public Archer(string name)
  19:       {
  20:          _name = name;
  21:          _current = new Position(0, 0);
  22:       }
  23:  
  24:       public void SetPosition(Position pos)
  25:       {
  26:          _current = pos;
  27:       }
  28:  
  29:       public void Attack(ref Position p)
  30:       {
  31:          // Archer can attack if use is between 4 and 10
  32:          // units away.
  33:          double distance = p.CalcDistance(_current);
  34:          string formatted = String.Format("{0:0.00}", distance); 
  35:          if (distance >= 4 && distance <= 10)
  36:             Console.WriteLine(_name 
  37:                + " is attacking! [" + formatted + "]");
  38:          else if (distance < 4)
  39:             Console.WriteLine(_name 
  40:                + " is too close to attack! [" + formatted + "]");
  41:          else if (distance > 10)
  42:             Console.WriteLine(_name 
  43:                + " is too far away to attack! [" + formatted + "]");
  44:       }
  45:    }
  46:  
  47:    public class Swordsman : IObserver
  48:    {
  49:       private string _name;
  50:       private Position _current;
  51:       public string Name
  52:       {
  53:          get { return _name; }
  54:          set { _name = value; }
  55:       }
  56:       public Position Current
  57:       {
  58:          get { return _current; }
  59:          set { _current = value; }
  60:       }
  61:  
  62:       public Swordsman(string name)
  63:       {
  64:          _name = name;
  65:          _current = new Position(0, 0);
  66:       }
  67:  
  68:       public void SetPosition(Position pos)
  69:       {
  70:          _current = pos;
  71:       }
  72:  
  73:       public void Attack(ref Position p)
  74:       {
  75:          // Swordsman can attack if the distance is 2 
  76:          // units or less from the player.
  77:          double distance = p.CalcDistance(_current);
  78:          string formatted = String.Format("{0:0.00}", distance); 
  79:          if (distance <= 2)
  80:             Console.WriteLine(_name 
  81:                + " is attacking! [" + formatted + "]");
  82:          else
  83:             Console.WriteLine(_name 
  84:                + " is too far away to attack! [" + formatted + "]");
  85:       }
  86:    }
  87: }

We now have all the pieces in place to put a little engine together to test things.  Our engine is keeping track of the player, and I added 2 archers and 2 sworsdmen as observers.  To make this a little easier visually, I came up with this graphic to represent everyone’s starting point.  (You don’t have to tell me, I already know that I suck at Photoshop)

 

Our engine is a simple console application, this is what it looks like after I add the observers and the player.

   1: namespace Observer
   2: {
   3:    class Program
   4:    {
   5:       static void Main(string[] args)
   6:       {
   7:          // Set up our observers.
   8:          Archer archer1 = new Archer("Robin Hood");
   9:          archer1.SetPosition(new Position(1, 3));
  10:  
  11:          Archer archer2 = new Archer("Legolas");
  12:          archer2.SetPosition(new Position(1, 7));
  13:  
  14:          Swordsman swordsman1 = new Swordsman("Conan");
  15:          swordsman1.SetPosition(new Position(7, 4));
  16:          
  17:          Swordsman swordsman2 = new Swordsman("Aragorn");
  18:          swordsman2.SetPosition(new Position(7, 6));
  19:  
  20:          // Set up the subject and register 
  21:          // the observers we created.
  22:          Player p = new Player();
  23:          p.RegisterObserver(archer1);
  24:          p.RegisterObserver(archer2);
  25:          p.RegisterObserver(swordsman1);
  26:          p.RegisterObserver(swordsman2);
  27:  
  28:          // Set the position of the player.
  29:          // This notifies all the observers
  30:          // and they will try to attack.
  31:          p.SetPosition(new Position(11, 5));
  32:  
  33:          Console.ReadLine();
  34:       }
  35:    }
  36: }

The observer pattern kicks in when we set the position of our player in line 31 above.  This is basically broadcasting to any observer that is listening where the player is, and they will all try to attack.  Running this application yields these results:

These results match what we expected based on our plot.  Since the player is too far away, none of the observers are able to attack.  Setting the position of the player triggers all of the observers to try and attack, and the Player class has no knowledge of either the Swordsman or Archer class.    Next we will move the player a little closer to everyone and see what happens.  To do this, we just make another call to our SetPosition method.

   1: p.SetPosition(new Position(9, 5));

This caused all of our players to try and attack again.    Since the archers are now in range, they can attack, while the swordsmen are still too far away……I’m guessing you get the idea now.  Now let’s try removing observers.  For the purposes of our example, I’m going to assume that Robin Hood took one for the team and is now out of the picture.  I can represent this by removing him as an observer.  I’ll do this above our previous SetPosition method and run the test application again.

   1: p.UnregisterObserver(archer1);
   2: p.SetPosition(new Position(9, 5));

 

Notice that since we unregistered him, he no longer responds to the player movement.   New observers can be added at any point just as easily by calling the RegisterObserver method.  The loose coupling allows us to create any kind of observer we want to, and the Player class doesn’t need to know about it.  All that it cares about is that any observer implements the IObserver interface that we created.

I thought that this was a cool example of using the Observer design pattern, and I hope that the implementation didn’t get lost in all the details.  I’ll admit it, once I got started with this example it was sort of fun.  I hope that this article helped shed some light on what the Observer pattern is, and how you could use it in an application. 

EDIT:

I decided to change up my example a little bit.  It seemed a little clunky to have the Player basically asked to be attacked, so I made it a little more generic.  The new IObserver interface looks like this:

   1: namespace Observer
   2: {
   3:    public interface IObserver<T>
   4:    {
   5:       void Notify(T subject);
   6:    }
   7: }

I then just set up the Swordman and Archer classes to inherit from IObserver<Position>, and then changed my Player class to call notify rather than attack like this:

   1: public void NofifyObservers()
   2: {
   3:    foreach (IObserver<Position> obs in _observers)
   4:       obs.Notify(_currentPosition);
   5: }

This also opens things up for me, I’m not restricted to just attacking the player.  For instance, this is now possible:

   1: namespace Observer
   2: {
   3:    public class FriendlyMage : IObserver<Position>
   4:    {
   5:       private string _name;
   6:       private Position _current;
   7:       public string Name
   8:       {
   9:          get { return _name; }
  10:          set { _name = value; }
  11:       }
  12:       public Position Current
  13:       {
  14:          get { return _current; }
  15:          set { _current = value; }
  16:       }
  17:  
  18:       public FriendlyMage(string name)
  19:       {
  20:          _name = name;
  21:          _current = new Position(0, 0);
  22:       }
  23:  
  24:       public void SetPosition(Position pos)
  25:       {
  26:          _current = pos;
  27:       }
  28:  
  29:       public void Notify(Position p)
  30:       {
  31:          // Mages can heal if the distance is 10 
  32:          // units or less from the player.
  33:          double distance = p.CalcDistance(_current);
  34:          string formatted = String.Format("{0:0.00}", distance);
  35:          if (distance <= 10)
  36:             Console.WriteLine(_name
  37:                + " is attempting to heal! [" + formatted + "]");
  38:          else
  39:             Console.WriteLine(_name
  40:                + " is too far away to heal! [" + formatted + "]");
  41:       }
  42:    }
  43: }

All I would have to do is drop him on the board and register him and he would do something completely different than attacking.  Nice!!

NOTE: I took the same example and implemented it using delegates and events….check it out.

kick it on DotNetKicks.com