Sunday 19 December 2010

Observer Pattern

"Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically."


In the code example below "Royston" and "Dot" are two Dogs who are very interested when their Owner puts their dog bowls on the floor. They implement the IHungryDog interface with its single Update method, which means they are the Observer abstractions on the diagram above.  When the Dog objects are constructed they are passed the Owner ("Clare") object so they can store a refererence to her. Owner is derived from DogFeeder and it contains the Attach(), Detach() and Notify() methods which makes it the Subject abstraction shown above.  Owner also contains a list of IHungryDogs so that when it somes to feeding time Owner can iterate through the list of subscribers and call Update on each one. When the Dog's Update methods are called, they can then go back to the Owner to get whatever data they need.  An alternative pattern would be one where the Update method has a parameter.  With this approach, Dog's wouldn't need to reference their Owner and there wouldn't be the extra trip from the Dog to the Owner to get the data item. 


using System;
using System.Collections.Generic;
 
namespace Observer
{
    class Program
    {
        static void Main(string[] args)
        {
            Owner clare = new Owner();
            clare.Attach(new Dog(clare, "Royston"));
            clare.Attach(new Dog(clare, "Dot"));
            clare.PutDogBowlsOnFloor(3);
            Console.ReadLine();
        }
 
        /// <summary>
        /// This is our Observer interface
        /// </summary>
        public interface IHungryDog
        {
            void Update();
        }
 
        /// <summary>
        /// This is our ConcreteObserver
        /// </summary>
        public class Dog : IHungryDog
        {
            private Owner _owner;
            private string _name;
 
            public Dog(Owner owner, string name)
            {
                _owner = owner;
                _name = name;
            }
 
            public void Update()
            {
                if (_name == "Dot")
                {
                    Console.WriteLine(
                        "Hi Dot here, I only want {0} scoops",
                        _owner.Scoops - 1);
                }
                else
                {
                    Console.WriteLine(
                        "Hi Royston here, I want at least {0} scoops",
                        _owner.Scoops + 1);
                }               
            }
        }       
 
        /// <summary>
        /// This is our abstract Subject
        /// </summary>
        public abstract class DogFeeder
        {
            List<IHungryDog> _dogs = new List<IHungryDog>();
 
            /// <summary>
            /// Attach a subscriber
            /// </summary>
            /// <param name="dog"></param>
            public void Attach(IHungryDog dog)
            {
                _dogs.Add(dog);
            }
 
            /// <summary>
            /// Detach a subscriber
            /// </summary>
            /// <param name="dog"></param>
            public void Detach(IHungryDog dog)
            {
                _dogs.Add(dog);
            }
 
            /// <summary>
            /// Notify each of our subscribers
            /// </summary>
            public void Notify()
            {
                foreach (IHungryDog dog in _dogs)
                {
                    dog.Update();
                }
            }           
        }
 
        /// <summary>
        /// This is our ConcreteSubject
        /// </summary>
        public class Owner : DogFeeder
        {
            public int Scoops { getset; }
 
            public void PutDogBowlsOnFloor(int scoops)
            {
                Scoops = scoops;
                Notify();
            }
        }
    }
}

Outputs:
Hi Royston here, I want at least 4 scoops
Hi Dot here, I only want 2 scoops

No comments:

Post a Comment