Improved Template Method Pattern

Introduction

The standard Template Method Pattern is well-known. For example, here is the pattern applied to a toy case involving language translation:

UML class diagram of the code below

#include <iostream>

#include <stdexcept>





class  Base

{



 protected:

  virtual const char*  TranslateDigit( unsigned int  x ) = 0;

  virtual const char*  LanguageName()                    = 0;



 public:

  unsigned int  x;

  void  Print()

  {

    if ( x > 9 )  throw  std::runtime_error( "Not a digit" );

    std::cout << "x is '" << TranslateDigit( x ) << "' in " << LanguageName() << std::endl;

  }



};





class  French : public  Base

{



 protected:

  const char*  TranslateDigit( unsigned int  x )

  {

    const char*  Array[] = { "zero", "un",  "deux", "trois", "quatre",

                             "cinq", "six", "sept", "huit",  "neuf" };

    return  Array[ x ];

  }

  const char*  LanguageName()

  {

    return  "French";

  }



};





class  English : public  Base

{



 protected:

  const char*  TranslateDigit( unsigned int  x )

  {

    const char*  Array[] = { "zero", "one", "two",   "three", "four",

                             "five", "six", "seven", "eight", "nine" };

    return  Array[ x ];

  }



};





class  AmericanEnglish : public  English

{



 protected:

  const char*  LanguageName()

  {

    return  "American English";

  }



};





class  BritishEnglish : public  English

{



 protected:

  const char*  LanguageName()

  {

    return  "British English";

  }



};

If Print() isn't inlined, you will only have one copy of the common code in program memory. If Print() is inlined, the optimizer may be able to combine this common code with code local to the call of Print() and improve performance. This can be useful, but now imagine that you are working on a real-time application and there are dozens or more virtual function calls inside Print(). It would be nice to have a different version of Print() created for each leaf class and make only a single virtual call to select the correct Print().

Alternative

Here is a version of the above hierarchy that accomplishes the same code reuse but scales better with increasing number of sub-functions:

UML class diagram of the code below

#include <iostream>

#include <stdexcept>





class  Base

{



 public:

  unsigned int  x;

  virtual void  Print() = 0;



};





template < class  T >  class  BaseTemplate : public  T

{



 public:

  void  Print()

  {

    if ( this->x > 9 )  throw  std::runtime_error( "Not a digit" );

    std::cout << "x is '" << this->TranslateDigit( this->x ) << "' in " << this->LanguageName() << std::endl;

  }



};





class  FrenchHelper : public  Base

{



 protected:

  const char*  TranslateDigit( unsigned int  x )

  {

    const char*  Array[] = { "zero", "un",  "deux", "trois", "quatre",

                             "cinq", "six", "sept", "huit",  "neuf" };

    return  Array[ x ];

  }

  const char*  LanguageName()

  {

    return  "French";

  }



};



typedef BaseTemplate< FrenchHelper >  French;





class  EnglishHelper : public  Base

{



 protected:

  const char*  TranslateDigit( unsigned int  x )

  {

    const char*  Array[] = { "zero", "one", "two",   "three", "four",

                             "five", "six", "seven", "eight", "nine" };

    return  Array[ x ];

  }



};





class  AmericanEnglishHelper : public  EnglishHelper

{



 protected:

  const char*  LanguageName()

  {

    return  "American English";

  }



};



typedef BaseTemplate< AmericanEnglishHelper >  AmericanEnglish;





class  BritishEnglishHelper : public  EnglishHelper

{



 protected:

  const char*  LanguageName()

  {

    return  "British English";

  }



};



typedef BaseTemplate< BritishEnglishHelper >  BritishEnglish;

At first glance the UML diagram looks more complicated but there is really only one template class added...the instantiated classes shown are simple typedefs. This code works exactly the same as the original, and even behaves similarly under common mistakes like omitting or misspelling a sub-function in one of the helper classes (which is not to say that either behave well).

There is one key (and unfortunate) difference that appears in the Print() function. Because the sub-functions and members of Base are members of the dependent class T, they must be prefixed by this-> when used. Alternately you can provide a using declaration for the sub-functions and any members you use; this leaves Print() unchanged from the original but is not supported by some older compilers. A using declaration or explicit scoping will also bypass any virtual function calls, but that isn't likely to be a problem if you are using this mechanism to eliminate such calls.


©2007 Donovan Hawkins <hawkins@cephira.com>

Cephira, Cephira.com, Cephira.org, Cephira.net, and CMPLT are unregistered trademarks of Donovan Hawkins