Observer Pattern Revisited – Delegates and Events

Posted by Jack Altiere on April 3rd, 2008

A few days ago, I wrote an article on how you could implement the Observer pattern in .NET.  The intent of this article was to show you how this design pattern worked, and I did it without using delegates and events.  This article will run through the same example, but this time the code will use a more traditional .NET event based solution.

Events in .NET allow an object to notify any other interested objects that a particular event has happened.  If you’ve done any forms development (either webforms or winforms) then you are already familiar with all sorts of events, like button clicks, selected index changes, etc.  A delegate is kind of like a type-safe function pointer in .NET.  They are commonly used in event based programming, and when using callback functions, etc.  The event based programming model in .NET inherently uses the observer pattern, with the registration / reregistering / notification of observers being handled by the framework.

I’m going to stick with the same example from the previous example.  We will have several NPC’s that want to react to a player based on the player’s position.  I’ll be using the same Position helper class to represent a 2-D position in the game engine, so if you need a refresher on how that works, please refer to the previous article.  Since we are using the framework to handle the details, we no longer need to use the ISubject and IObserver interfaces that we created.  Instead the Player class has a public event to indicate that they have moved position.   The new Player class now looks like this:

   1: public class Player
   2: {
   3:    private Position _currentPosition;
   4:       
   5:    public Position CurrentPosition
   6:    {
   7:       get { return _currentPosition; }
   8:       set { _currentPosition = value; }
   9:    }
  10:  
  11:    // Set up the event that indicates that the 
  12:    //player changed position.
  13:    public delegate void PositionMovedHandler(Player p);
  14:    public event PositionMovedHandler PositionMoved = delegate { }; 
  15:  
  16:    public Player()
  17:    {
  18:  
  19:    }
  20:  
  21:    // This is the key event in our scenario.  The only thing
  22:    // that the observers care about is the position of the 
  23:    // player, so we notify them when it changes.
  24:    public void SetPosition(Position pos)
  25:    {
  26:       _currentPosition = pos;
  27:       Console.WriteLine("Player is at " + pos.ToString());
  28:  
  29:       // Fire off the position changed event 
  30:       // if there is someone listening.
  31:       PositionMoved(this);
  32:    }
  33: }

Again, since we are letting the framework handle the notifications, we don’t have to handle any of that in our Player class.  There will also be changes to our Archer / Swordsman / FriendlyMage classes from the previous example.  While these classes no longer need to implement our IObserver interface, they will need to know about the player event, and what to do if the event happens.  I did this by adding a public method to each class allowing me to subscribe to the PositionMoved event generated by our Player class.  This is what the Archer class now looks like:

   1: public class Archer 
   2: {
   3:    private string _name;
   4:    private Position _current;
   5:    public string Name
   6:    {
   7:       get { return _name; }
   8:       set { _name = value; }
   9:    }
  10:    public Position Current
  11:    {
  12:       get { return _current; }
  13:       set { _current = value; }
  14:    }
  15:  
  16:    public Archer(string name)
  17:    {
  18:       _name = name;
  19:       _current = new Position(0, 0);
  20:    }
  21:  
  22:    public void SetPosition(Position pos)
  23:    {
  24:       _current = pos;
  25:    }
  26:  
  27:    // This method allows the Archer to react to the 
  28:    // PositionMoved event.
  29:    public void Subscribe(Player p)
  30:    {
  31:       p.PositionMoved 
  32:         += new Player.PositionMovedHandler(Notify);
  33:    }
  34:  
  35:    // This is the same function as in the previous
  36:    // example, it is now acting as a
  37:    // traditional event handler.
  38:    public void Notify(Player p)
  39:    {
  40:       // Archer can attack if use is between 4 and 10
  41:       // units away.
  42:       double distance = p.CurrentPosition.CalcDistance(_current);
  43:       string formatted = String.Format("{0:0.00}", distance);
  44:       if (distance >= 4 && distance <= 10)
  45:          Console.WriteLine(_name
  46:             + " is attacking! [" + formatted + "]");
  47:       else if (distance < 4)
  48:          Console.WriteLine(_name
  49:             + " is too close to attack! [" + formatted + "]");
  50:       else if (distance > 10)
  51:          Console.WriteLine(_name
  52:             + " is too far away to attack! [" + formatted + "]");
  53:    }
  54: }

The same Subscribe method needs to be place in the Swordsman and FriendlyMage classes.  This seems like too much maintenance, and we will address this problem in a little while.  Our classes representing our NCP characters now have to be aware that the PositionMoved event is available from the Player object.  This really isn’t a huge deal though, since they already needed to know information about the Player object in order to calculate the distance between them. (they use the CurrentPosition property of the player in the Notify function)

This actually makes our solution more flexible than when we used our own interfaces.  The same subscribe method could be used to handle any number of events generated by the player.  We could have as many handlers as we needed.  In our previous example it could get ugly trying to support multiple events in a clean fashion.  The reason for this is that by using events, the .NET framework is responsible for keeping track of who is subscribed to each event.  In the previous example. that burden would fall on the Player object.

The last thing we need to cover is how to subscribe to this events from within the game engine.  This turns out to be as easy as calling our Subscribe function, like this:

   1: class Program
   2: {
   3:    static void Main(string[] args)
   4:    {      
   5:       // Set up the player.
   6:       Player p = new Player();
   7:  
   8:       // Set up the NPC's.
   9:       // Calling the Subscribe method registers
  10:       // us to use the event.
  11:       Archer archer1 = new Archer("Robin Hood");
  12:       archer1.SetPosition(new Position(1, 3));
  13:       archer1.Subscribe(p);
  14:  
  15:       Archer archer2 = new Archer("Legolas");
  16:       archer2.SetPosition(new Position(1, 7));
  17:       archer2.Subscribe(p);
  18:  
  19:       Swordsman swordsman1 = new Swordsman("Conan");
  20:       swordsman1.SetPosition(new Position(7, 4));
  21:       swordsman1.Subscribe(p);
  22:  
  23:       Swordsman swordsman2 = new Swordsman("Aragorn");
  24:       swordsman2.SetPosition(new Position(7, 6));
  25:       swordsman2.Subscribe(p);
  26:  
  27:       FriendlyMage mage = new FriendlyMage("Gandalf");
  28:       mage.SetPosition(new Position(11, 9));
  29:       mage.Subscribe(p);
  30:  
  31:       // Move the player.  This fires off the 
  32:       // event we created in our Player class.
  33:       p.SetPosition(new Position(11, 5));
  34:    
  35:       Console.ReadLine();
  36: }

Running this application gives us the same result we would expect:

One other thing I should mention to keep things consistent.  In the last example I was able to unregister an NPC from reacting to events.  That is a simple change here, all we have to do is add an unsubscribe function to our Archer, Swordsman, and FriendlyMage classes, like this:

   1: public void UnSubscribe(Player p)
   2: {
   3:    p.PositionMoved -= new Player.PositionMovedHandler(Notify);
   4: }

This allows us to remove someone any time we choose in the engine, like this:

   1: // This takes the NPC off
   2: // the notification list for 
   3: // Player events.
   4: archer1.UnSubscribe(p);

Remember when I said we’d get back to the problem of maintenance from modifying all of those classes?  The last thing I want to cover is how to clean up our design here a little bit.  Since all of our NPC classes have the same functions, we can create a base class called NPC, and this class could deal with handling the subscribing and unsubscribing to the events….which would clean up our implementation a lot.  This is what our base class would look like:

   1: public abstract class NPC
   2:  
   3:   // These methods are common to all NPC's.
   4:   public void Subscribe(Player p)
   5:   {
   6:      p.PositionMoved += new Player.PositionMovedHandler(Notify);
   7:   }
   8:   public void Unsubscribe(Player p)
   9:   {
  10:      p.PositionMoved -= new Player.PositionMovedHandler(Notify);
  11:   }
  12:  
  13:   // Each NPC will have a unique implementation of this.
  14:   public abstract void Notify(Player p);

After this change, we would make all of our NPC classes inherit from this class, so they would look like this:

   1: public class Archer : NPC
   2: {
   3:    private string _name;
   4:    private Position _current;
   5:    public string Name
   6:    {
   7:       get { return _name; }
   8:       set { _name = value; }
   9:    }
  10:    public Position Current
  11:    {
  12:       get { return _current; }
  13:       set { _current = value; }
  14:    }
  15:  
  16:    public Archer(string name)
  17:    {
  18:       _name = name;
  19:       _current = new Position(0, 0);
  20:    }
  21:  
  22:    public void SetPosition(Position pos)
  23:    {
  24:       _current = pos;
  25:    }
  26:  
  27:    // We are now overriding the definition
  28:    // from our base class.
  29:    public override void Notify(Player p)
  30:    {
  31:       // Archer can attack if use is between 4 and 10
  32:       // units away.
  33:       double distance = p.CurrentPosition.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: }

Making this change cleans up the design, and we would only have to make changes regarding event subscription in the base class rather than in every concrete instance.  As you can see, the .NET framework has built in support for this design pattern right out of the box!!

kick it on DotNetKicks.com

Using the Observer Pattern

Posted by Jack Altiere on April 1st, 2008

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

Downloads

Twitter Updates

    Top Commentators

    • No commentators.

    Copyright © 2007 Jack Altiere. All rights reserved.