Monday 20 December 2010

Template Method Pattern

"Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure."


This is a very understandable pattern and unlike many of the other patterns which emphasise a preference for composition over inheritance, the power of this pattern is entirely down to inheritance from an abstract base class.  The base class contains a "controlling or co-ordinating method" which strings together a sequence of other methods.  This is a set of steps which all inheritors need to follow i.e. it is the template method!  The key to this pattern is that these other methods are carefully marked as being abstract, virtual or normal (non-overridable) in the base class.
  • Normal non-overridable methods are ones where the deriving classes don't get the chance to change the base implementation. In the code example below the GetDressed() method cannot be overridden.  The co-ordinating method (MorningRoutine()) should also be one of these methods – i.e. it should never be virtual because the point of the pattern is that all deriving classes should follow the sequence of steps in the base class implementation*.
  • Abstract methods are ones where there is no sensible implementation in the abstract class – it is entirely down to the derived class to provide the implementation.  In the example below the ChooseRoute() method is one such example.
  • The half-way house is provided by virtual methods which have an implementation but can be overridden. In the example below the ChooseLead() method has a default implementation, but it can be overridden by the deriving class.
*Incidentally, In Java the co-ordinating method can be marked as final which means a derived class cannot contain a method with the same signature.  C# is not as strict because even though the method is not virtual, it can still be hidden with the new keyword.
using System;
 
namespace TemplateMethod
{
    class Program
    {
        static void Main(string[] args)
        {
            MorningWalkJess jason = new JasonMorningWalkJess();
            WalkTheBeast(jason);
            Console.ReadLine();
        }
 
        /// <summary>
        /// Passing the walker as a base class member demonstrates that
        /// the methods will be resolved polymorphically
        /// </summary>
        /// <param name="walker"></param>
        private static void WalkTheBeast(MorningWalkJess walker)
        {
            walker.MorningRoutine();
        }
    }
 
    public abstract class MorningWalkJess
    {
        protected enum LeadType
        {
            Extending,
            Short
        }
 
        protected LeadType _lead;
 
        public void MorningRoutine()
        {
            GetDressed();
            ChooseLead();
            ChooseRoute();
            Walk();
            WipePaws();
        }
 
        /// <summary>
        /// This method is not virtual. This (base) class provides
       
/// the implementation and we can't override it.
       
/// I wouldn't want to get arrested and it was -4          
       
/// degrees C this morning!
        /// </summary>
        private void GetDressed()
        {
            Console.WriteLine("Don't want to get arrested");
        }
       
        /// <summary>
        /// This method is virtual -      
       
///  In our derived class we can either go with the 
        /// default in this base class or we can override it
       
/// and choose our own lead
        /// </summary>
        protected virtual void ChooseLead()
        {
            _lead = LeadType.Extending;
            Console.WriteLine("I've stuck with the extending lead");
        }
 
        /// <summary>
        /// This method is abstract -
       
/// its down to the walker to choose the route
        /// </summary>
        protected abstract void ChooseRoute();
   
        /// <summary>
        /// No need to override - derived classes will just reuse this 
        /// implementation
        /// </summary>
        public void Walk()
        {
            Console.WriteLine("Walk the beast");
        }
 
        /// <summary>
        /// Leave it up to the derived class to decide whether to go with the
        /// default or override
        /// </summary>
        protected virtual void WipePaws()
        {
            Console.WriteLine("Wipe the beast's paws");
        }
    }
 
    /// <summary>
    /// Deriving class overrides what we have to derive (abstract methods) 
    /// and what we want to derive (virtual methods).
    /// </summary>
    public class JasonMorningWalkJess : MorningWalkJess
    {
        protected override void ChooseRoute()
        {
           Console.WriteLine(
           "Think I'll go through the field with the donkey");
        }
 
        protected override void WipePaws()
        {
            Console.WriteLine(
            "Donkey field isn't very muddy, so I won't bother");
        }
    }
}


Outputs:

Don't want to get arrested
I've stuck with the extending lead
Think I'll go through the field with the donkey
Walk the beast
Donkey field isn't very muddy, so I won't bother
 

No comments:

Post a Comment