

Download the source for the Template Pattern.
Encapsulation is one of the fundamental concepts of object-oriented programming: by hiding the internal mechanisms and data structure of an object behind an interface, users need only know what that object does and not how it is implemented with the added benefit of reducing system complexity. With the Template pattern we can encapsulate pieces of algorithms so they can be hooked into by subclasses.
Why encapsulate algorithms? By encapsulating algorithms that are essentially the same we can provide a concrete skeleton of steps that must be taken while allowing for multiple implementations by providing the implementation of one or more steps in a subclass. For example, assume that you run a shop that sells coffee and tea, you might have a Tea class like:
class Tea {
public function prepareRecipe() {
$this->boilWater();
$this->steepTeaBag();
$this->pourInCup();
$this->addLemon();
}
public function boilWater() {
echo "Boiling water";
}
public function steepTeaBag() {
echo "Steeping the tea";
}
public function pourInCup() {
echo "Pouring into cup";
}
public function addLemon() {
echo "Adding Lemon";
}
}
The Coffee class would look very similar with similar supporting methods:
class Coffee {
public function prepareRecipe() {
$this->boilWater();
$this->brewCoffeeGrounds();
$this->pourInCup();
$this->addSugarandMilk();
}
...other methods here...
}
Notice the prepareRecipe() method: the algorithm in the Coffee and Tea classes are very similar, both objects must boil some water and pour the beverage in a cup, the only differences are in the ingredients that are used/added. By generalizing the steepTeaBag()/brewCoffeeGrounds() and addLemon()/addSugarandMilk() methods can we make an abstract class to represent multiple types of brewed beverages…
Our new class will contain a method with our finalized algorithm, a couple of defined methods for shared steps and a couple of abstract methods to be implemented by subclasses, here is our BrewedBeverage.class.php:
abstract class BrewedBeverage {
final public function prepareRecipe() {
$this->boilWater();
$this->brew();
$this->pourInCup();
$this->addCondiments();
}
abstract function brew();
abstract function addCondiments();
public function boilWater() {
echo "Boiling water";
}
public function pourInCup() {
echo "Pouring into cup";
}
}
By making the prepareRecipe() method final, subclasses cannot redefine its functionality. The new Tea object created from the BrewedBeverage class, Tea.class.php:
class Tea extends BrewedBeverage {
public function brew() {
echo "Steeping the tea";
}
public function addCondiments() {
echo "Adding Lemon";
}
}
A coffee object would look very similar while implementing a different set of instructions. Besides just using Templates for the creation of encapsulated, concrete algorithms, we can also make some of those steps optional through the use of hooks…
To add a hook to our BrewedBeverage class we’ll create a new method called wantsCondiments() and use it to control access to our addCondiments() method. Here is BrewedBeverageWithHook.class.php:
abstract class BrewedBeverageWithHook {
final public function prepareRecipe() {
$this->boilWater();
$this->brew();
$this->pourInCup();
if($this->wantsCondiments()) {
$this->addCondiments();
}
}
abstract function brew();
abstract function addCondiments();
public function boilWater() {
echo "Boiling water";
}
public function pourInCup() {
echo "Pouring into cup";
}
public function wantsCondiments() {
return true;
}
}
You’ll notice that the default implementation for wantsCondiments() returns true so that if it is not redefined in a subclass we will always execute addCondiments(). To implement the hook in our coffee class we’ll add one more method called getUserInput() which will test to see if the user wants to add condiments to their drink. A sample implementation of Coffee.class.php:
class Coffee extends BrewedBeverageWithHook {
public function brew() {
echo "Dripping coffee through filter";
}
public function addCondiments() {
echo "Adding Milk and Sugar";
}
public function wantsCondiments() {
$answer = $this->getUserInput();
if($answer) {
return true;
} else {
return false;
}
}
public function getUserInput() {
$answer = NULL;
//do some stuff to get input, I'm gonna be lazy
srand(time());
$num = rand();
if($num % 3) {
$answer = false;
} else {
$answer = true;
}
return $answer;
}
}
The way getUserInput() is implemented now doesn’t actually require input but should return false about 1/3rd of the time for testing purposes. Speaking of testing…
Our test file, index.php:
function __autoload($class_name) {
require_once 'classes/' . $class_name . '.class.php';
}
$tea = new Tea();
$coffee = new Coffee();
echo "Making the Tea...";
$tea->prepareRecipe();
echo "Making the Coffee...";
$coffee->prepareRecipe();
You should get something like:
Making the Tea...
Boiling water
Steeping the tea
Pouring into cup
Adding Lemon
Making the Coffee...
Boiling water
Dripping coffee through filter
Pouring into cup
Adding Milk and Sugar
Of course your exact results will depend on what time it is, based on our current getUserInput() but it looks like we have successfully encapsulated our recipe preparation algorithm. Templates are used quite often and can show up in many forms including sorting algorithms and may be used to add immediate value to software or added with an eye towards future enhancements. Another great OO programming tool.
Download the source for the Template Pattern.