C++ Tutorial: The Template Pattern

Introduction

This is a simple article on Template Pattern. Template Pattern is one of the widely used patterns. It allows us to set up the outline of an algorithm and leaves the details of the implementation later. Template Pattern is a behavior pattern. Let us have a look at the UML diagram below.

Template Pattern UML Diagram

TemplateMethod is a public non-virtual method while Operation is a protected pure-virtual method whose behaviour will be defined in the ConcreteClassA and ConcreteClassB. Even though TemplateMethod is a non-virtual method but due to its call to the Operation method, its behaviour is partially defined by Operation method in the polymorphic derived class.

A Simple Non-Templated Example

Let us look at the problem this pattern can solve. I have a TextOut base class which is meant to be used in the derived classes, say Console, Debug and File to print out the contents in console, debugger and file logging, respectively. The TextOut class has 10 virtual Print methods which I need to override in my derived classes. These Print method use the Box object as their parameters. The Box class is just a simple class whose constructor takes in a primitive data type(POD), and converts them to std::wstring.

class Textout
{
public:
	virtual void Print(
		const wchar_t* fmt,
		Box box1 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2, Box box3 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2, Box box3, Box box4 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2, Box box3,
		Box box4, Box box5 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2, Box box3,
		Box box4, Box box5, Box box6 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2, Box box3, Box box4,
		Box box5, Box box6, Box box7 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2, Box box3, Box box4,
		Box box5, Box box6, Box box7, Box box8 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2, Box box3, Box box4,
		Box box5, Box box6, Box box7, Box box8, Box box9 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2, Box box3, Box box4, Box box5,
		Box box6, Box box7, Box box8, Box box9, Box box10 );

	std::wstring GetFormattedString();

	std::wstring StrReplace(
		std::wstring& fmtstr,
		const std::vector<std::wstring>& vs );

	std::wstring StrAnchor( size_t i );
};

These are 2 examples of what the Print methods are doing. I will not show the rest of them as they are similar in nature.

void Textout::Print(
	const wchar_t* fmt,
	Box box1 )
{
	std::wstring wsfmtstr = fmt;

	std::vector<std::wstring> vs;
	vs.push_back( box1.ToString() );

	m_str = StrReplace( wsfmtstr, vs );
}

void Textout::Print(
	const wchar_t* fmt,
	Box box1, Box box2 )
{
	std::wstring wsfmtstr = fmt;

	std::vector<std::wstring> vs;
	vs.push_back( box1.ToString() );
	vs.push_back( box2.ToString() );

	m_str = StrReplace( wsfmtstr, vs );
}

Below is the declaration of the Console class:

class Console : public Textout
{
public:
	virtual void Print(
		const wchar_t* fmt,
		Box box1 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2, Box box3 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2, Box box3, Box box4 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2, Box box3,
		Box box4, Box box5 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2, Box box3,
		Box box4, Box box5, Box box6 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2, Box box3, Box box4,
		Box box5, Box box6, Box box7 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2, Box box3, Box box4,
		Box box5, Box box6, Box box7, Box box8 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2, Box box3, Box box4,
		Box box5, Box box6, Box box7, Box box8, Box box9 );

	virtual void Print(
		const wchar_t* fmt,
		Box box1, Box box2, Box box3, Box box4, Box box5,
		Box box6, Box box7, Box box8, Box box9, Box box10 );
};

Below is an example how I would override the Print methods without using template pattern:

void Console::Print(
	const wchar_t* fmt,
	Box box1 )
{
	Textout::Print( fmt, box1 );
	std::wcout << GetFormattedString();
}

void Console::Print(
	const wchar_t* fmt,
	Box box1, Box box2 )
{
	Textout::Print( fmt, box1, box2 );
	std::wcout << GetFormattedString();
}

Remember I have to repeat this boilerplated code 8 more times. Here is an example on how to call the Console::Print method.

#include "Console.h"

int _tmain(int argc, _TCHAR* argv[])
{
	Console console;
	console.Print(L"{0} prints {1}\n", L"Console", 123);

	return 0;
}

More by Author

Must Read