Saturday, October 11, 2014

Observer Pattern

Today I've decided to recreate the observer pattern example that I accidentally destroyed here. In it there are four varieties of animals which inherit from a common base class which looks like this:

namespace NuclearWar.Animals
{
   public abstract class Animal
   {
      public virtual bool IsHiding { get; set; }
      public virtual bool IsSickly { get; set; }
      
      public Animal()
      {
         IsHiding = false;
         IsSickly = false;
      }
   }
}

 
 

The four kinds of Animal are:

  1. namespace NuclearWar.Animals
    {
       public class Asp : Animal
       {
          public Asp()
          {
          }
          
          public void AbsorbRadiation()
          {
             IsSickly = true;
          }
       }
    }
     
     
  2. namespace NuclearWar.Animals
    {
       public class Bug : Animal
       {
          public Bug()
          {
             IsHiding = true;
          }
          
          public void AbsorbRadiation()
          {
             IsSickly = true;
             IsHiding = false;
          }
       }
    }
     
     
  3. namespace NuclearWar.Animals
    {
       public class Cat : Animal
       {
          public Cat()
          {
          }
          
          public void AbsorbRadiation()
          {
             IsSickly = true;
             IsHiding = true;
          }
       }
    }
     
     
  4. namespace NuclearWar.Animals
    {
       public class Dog : Animal
       {
          private bool _isRoamingTheCountrysideFoamingAtTheMouth;
          
          public bool IsRoamingTheCountrysideFoamingAtTheMouth
          {
             get
             {
                return _isRoamingTheCountrysideFoamingAtTheMouth;
             }
             set
             {
                _isRoamingTheCountrysideFoamingAtTheMouth = value;
                if (value)
                {
                   IsHiding = false;
                }
             }
          }
          
          public Dog()
          {
             IsRoamingTheCountrysideFoamingAtTheMouth = false;
          }
          
          public void AbsorbRadiation()
          {
             IsSickly = true;
             IsRoamingTheCountrysideFoamingAtTheMouth = true;
          }
       }
    }

 
 

Another type in my application is NuclearBomb. At first ReSharper put Antlr.Runtime.Misc as the namespace directive at the top of this file instead of System in the name of empowering Action and I had to include Antlr3.Runtime as a reference in the test project where the test I am about to show you lives, but then I caught the mistake.

using System;
namespace NuclearWar.Machines
{
   public class NuclearBomb
   {
      public Action Detonate;
   }
}

 
 

In this test, I make bunch of animals and then set off a NuclearBomb to make them all sick. This test passes.

using Microsoft.VisualStudio.TestTools.UnitTesting;
using NuclearWar.Animals;
using NuclearWar.Machines;
namespace NuclearWar.Tests.Machines
{
   [TestClass]
   public class NuclearBombTest
   {
      [TestMethod]
      public void Test()
      {
         
//Act 1
         Asp lucifer = new Asp();
         Bug gregor = new Bug();
         Cat felix = new Cat();
         Dog spot = new Dog();
         
         Assert.AreEqual(lucifer.IsHiding, false);
         Assert.AreEqual(lucifer.IsSickly, false);
         
         Assert.AreEqual(gregor.IsHiding, true);
         Assert.AreEqual(gregor.IsSickly, false);
         
         Assert.AreEqual(felix.IsHiding, false);
         Assert.AreEqual(felix.IsSickly, false);
         
         Assert.AreEqual(spot.IsHiding, false);
         Assert.AreEqual(spot.IsSickly, false);
         Assert.AreEqual(spot.IsRoamingTheCountrysideFoamingAtTheMouth, false);
         
         
         
//Act 2
         NuclearBomb littleBoy = new NuclearBomb();
         littleBoy.Detonate = lucifer.AbsorbRadiation;
         littleBoy.Detonate += gregor.AbsorbRadiation;
         littleBoy.Detonate += felix.AbsorbRadiation;
         littleBoy.Detonate += spot.AbsorbRadiation;
         
         Assert.AreEqual(lucifer.IsHiding, false);
         Assert.AreEqual(lucifer.IsSickly, false);
         
         Assert.AreEqual(gregor.IsHiding, true);
         Assert.AreEqual(gregor.IsSickly, false);
         
         Assert.AreEqual(felix.IsHiding, false);
         Assert.AreEqual(felix.IsSickly, false);
         
         Assert.AreEqual(spot.IsHiding, false);
         Assert.AreEqual(spot.IsSickly, false);
         Assert.AreEqual(spot.IsRoamingTheCountrysideFoamingAtTheMouth, false);
         
         
         
//Act 3
         littleBoy.Detonate();
         
         Assert.AreEqual(lucifer.IsHiding, false);
         Assert.AreEqual(lucifer.IsSickly, true);
         
         Assert.AreEqual(gregor.IsHiding, false);
         Assert.AreEqual(gregor.IsSickly, true);
         
         Assert.AreEqual(felix.IsHiding, true);
         Assert.AreEqual(felix.IsSickly, true);
         
         Assert.AreEqual(spot.IsHiding, false);
         Assert.AreEqual(spot.IsSickly, true);
         Assert.AreEqual(spot.IsRoamingTheCountrysideFoamingAtTheMouth, true);
      }
   }
}

 
 

Let's step back through this slowly a second time. I tell a story here in three parts, and first of all our heros are

  1. an asp named Lucifer,
  2. a bug named Gregor (winking at Kafka),
  3. a cat named Felix,
  4. and a dog named Spot.

They start their lives in good health as seen here:

Asp lucifer = new Asp();
Bug gregor = new Bug();
Cat felix = new Cat();
Dog spot = new Dog();
 
Assert.AreEqual(lucifer.IsHiding, false);
Assert.AreEqual(lucifer.IsSickly, false);
 
Assert.AreEqual(gregor.IsHiding, true);
Assert.AreEqual(gregor.IsSickly, false);
 
Assert.AreEqual(felix.IsHiding, false);
Assert.AreEqual(felix.IsSickly, false);
 
Assert.AreEqual(spot.IsHiding, false);
Assert.AreEqual(spot.IsSickly, false);
Assert.AreEqual(spot.IsRoamingTheCountrysideFoamingAtTheMouth, false);

 
 

Next our villian is introduced, a NuclearBomb named littleBoy. All of the AbsorbRadiation methods for the animals are added to littleBoy's Detonate action, but Detonate is not yet called so all of the animals remain in good health.

NuclearBomb littleBoy = new NuclearBomb();
littleBoy.Detonate = lucifer.AbsorbRadiation;
littleBoy.Detonate += gregor.AbsorbRadiation;
littleBoy.Detonate += felix.AbsorbRadiation;
littleBoy.Detonate += spot.AbsorbRadiation;
 
Assert.AreEqual(lucifer.IsHiding, false);
Assert.AreEqual(lucifer.IsSickly, false);
 
Assert.AreEqual(gregor.IsHiding, true);
Assert.AreEqual(gregor.IsSickly, false);
 
Assert.AreEqual(felix.IsHiding, false);
Assert.AreEqual(felix.IsSickly, false);
 
Assert.AreEqual(spot.IsHiding, false);
Assert.AreEqual(spot.IsSickly, false);
Assert.AreEqual(spot.IsRoamingTheCountrysideFoamingAtTheMouth, false);

 
 

Finally, the bomb goes off and all of the animals get sick, each in their own way. Observe:

littleBoy.Detonate();

Assert.AreEqual(lucifer.IsHiding, false);
Assert.AreEqual(lucifer.IsSickly, true);
 
Assert.AreEqual(gregor.IsHiding, false);
Assert.AreEqual(gregor.IsSickly, true);
 
Assert.AreEqual(felix.IsHiding, true);
Assert.AreEqual(felix.IsSickly, true);
 
Assert.AreEqual(spot.IsHiding, false);
Assert.AreEqual(spot.IsSickly, true);
Assert.AreEqual(spot.IsRoamingTheCountrysideFoamingAtTheMouth, true);

 
 

lucifer, gregor, felix, and spot, all independent objects from littleBoy, were all listening for littleBoy to act. That's the observer pattern!

No comments:

Post a Comment