Conceptual Example
This example illustrates the structure of the Template Method design pattern and focuses on the following questions:
What classes does it consist of?
What roles do these classes play?
In what way the elements of the pattern are related?
After learning about the pattern’s structure it’ll be easier for you to grasp the following example, based on a real-world PHP use case.
index.php: Conceptual example
<?php
namespace RefactoringGuru\TemplateMethod\Conceptual;
/**
* The Abstract Class defines a template method that contains a skeleton of some
* algorithm, composed of calls to (usually) abstract primitive operations.
*
* Concrete subclasses should implement these operations, but leave the template
* method itself intact.
*/
abstract class AbstractClass
{
/**
* The template method defines the skeleton of an algorithm.
*/
final public function templateMethod(): void
{
$this->baseOperation1();
$this->requiredOperations1();
$this->baseOperation2();
$this->hook1();
$this->requiredOperation2();
$this->baseOperation3();
$this->hook2();
}
/**
* These operations already have implementations.
*/
protected function baseOperation1(): void
{
echo "AbstractClass says: I am doing the bulk of the work\n";
}
protected function baseOperation2(): void
{
echo "AbstractClass says: But I let subclasses override some operations\n";
}
protected function baseOperation3(): void
{
echo "AbstractClass says: But I am doing the bulk of the work anyway\n";
}
/**
* These operations have to be implemented in subclasses.
*/
abstract protected function requiredOperations1(): void;
abstract protected function requiredOperation2(): void;
/**
* These are "hooks." Subclasses may override them, but it's not mandatory
* since the hooks already have default (but empty) implementation. Hooks
* provide additional extension points in some crucial places of the
* algorithm.
*/
protected function hook1(): void { }
protected function hook2(): void { }
}
/**
* Concrete classes have to implement all abstract operations of the base class.
* They can also override some operations with a default implementation.
*/
class ConcreteClass1 extends AbstractClass
{
protected function requiredOperations1(): void
{
echo "ConcreteClass1 says: Implemented Operation1\n";
}
protected function requiredOperation2(): void
{
echo "ConcreteClass1 says: Implemented Operation2\n";
}
}
/**
* Usually, concrete classes override only a fraction of base class' operations.
*/
class ConcreteClass2 extends AbstractClass
{
protected function requiredOperations1(): void
{
echo "ConcreteClass2 says: Implemented Operation1\n";
}
protected function requiredOperation2(): void
{
echo "ConcreteClass2 says: Implemented Operation2\n";
}
protected function hook1(): void
{
echo "ConcreteClass2 says: Overridden Hook1\n";
}
}
/**
* The client code calls the template method to execute the algorithm. Client
* code does not have to know the concrete class of an object it works with, as
* long as it works with objects through the interface of their base class.
*/
function clientCode(AbstractClass $class)
{
// ...
$class->templateMethod();
// ...
}
echo "Same client code can work with different subclasses:\n";
clientCode(new ConcreteClass1());
echo "\n";
echo "Same client code can work with different subclasses:\n";
clientCode(new ConcreteClass2());
Output.txt: Execution result
Same client code can work with different subclasses:
AbstractClass says: I am doing bulk of the work
ConcreteClass1 says: Implemented Operation1
AbstractClass says: But I let subclasses to override some operations
ConcreteClass1 says: Implemented Operation2
AbstractClass says: But I am doing bulk of the work anyway
Same client code can work with different subclasses:
AbstractClass says: I am doing bulk of the work
ConcreteClass2 says: Implemented Operation1
AbstractClass says: But I let subclasses to override some operations
ConcreteClass2 says: Overridden Hook1
ConcreteClass2 says: Implemented Operation2
AbstractClass says: But I am doing bulk of the work anyway
Real World Example
In this example, the Template Method pattern defines a skeleton of the algorithm of message posting to social networks. Each subclass represents a separate social network and implements all the steps differently, but reuses the base algorithm.
index.php: Real world example
<?php
namespace RefactoringGuru\TemplateMethod\RealWorld;
/**
* The Abstract Class defines the template method and declares all its steps.
*/
abstract class SocialNetwork
{
protected $username;
protected $password;
public function __construct(string $username, string $password)
{
$this->username = $username;
$this->password = $password;
}
/**
* The actual template method calls abstract steps in a specific order. A
* subclass may implement all of the steps, allowing this method to actually
* post something to a social network.
*/
public function post(string $message): bool
{
// Authenticate before posting. Every network uses a different
// authentication method.
if ($this->logIn($this->username, $this->password)) {
// Send the post data. All networks have different APIs.
$result = $this->sendData($message);
// ...
$this->logOut();
return $result;
}
return false;
}
/**
* The steps are declared abstract to force the subclasses to implement them
* all.
*/
abstract public function logIn(string $userName, string $password): bool;
abstract public function sendData(string $message): bool;
abstract public function logOut(): void;
}
/**
* This Concrete Class implements the Facebook API (all right, it pretends to).
*/
class Facebook extends SocialNetwork
{
public function logIn(string $userName, string $password): bool
{
echo "\nChecking user's credentials...\n";
echo "Name: " . $this->username . "\n";
echo "Password: " . str_repeat("*", strlen($this->password)) . "\n";
simulateNetworkLatency();
echo "\n\nFacebook: '" . $this->username . "' has logged in successfully.\n";
return true;
}
public function sendData(string $message): bool
{
echo "Facebook: '" . $this->username . "' has posted '" . $message . "'.\n";
return true;
}
public function logOut(): void
{
echo "Facebook: '" . $this->username . "' has been logged out.\n";
}
}
/**
* This Concrete Class implements the Twitter API.
*/
class Twitter extends SocialNetwork
{
public function logIn(string $userName, string $password): bool
{
echo "\nChecking user's credentials...\n";
echo "Name: " . $this->username . "\n";
echo "Password: " . str_repeat("*", strlen($this->password)) . "\n";
simulateNetworkLatency();
echo "\n\nTwitter: '" . $this->username . "' has logged in successfully.\n";
return true;
}
public function sendData(string $message): bool
{
echo "Twitter: '" . $this->username . "' has posted '" . $message . "'.\n";
return true;
}
public function logOut(): void
{
echo "Twitter: '" . $this->username . "' has been logged out.\n";
}
}
/**
* A little helper function that makes waiting times feel real.
*/
function simulateNetworkLatency()
{
$i = 0;
while ($i < 5) {
echo ".";
sleep(1);
$i++;
}
}
/**
* The client code.
*/
echo "Username: \n";
$username = readline();
echo "Password: \n";
$password = readline();
echo "Message: \n";
$message = readline();
echo "\nChoose the social network to post the message:\n" .
"1 - Facebook\n" .
"2 - Twitter\n";
$choice = readline();
// Now, let's create a proper social network object and send the message.
if ($choice == 1) {
$network = new Facebook($username, $password);
} elseif ($choice == 2) {
$network = new Twitter($username, $password);
} else {
die("Sorry, I'm not sure what you mean by that.\n");
}
$network->post($message);
Output.txt: Execution result
Username:
> neo
Password:
> 123123
Message:
> What is the Matrix?
Choose the social network to post the message:
1 - Facebook
2 - Twitter
> 1
Checking user's credentials...
Name: neo
Password: ******
.....
Facebook: 'neo' has logged in successfully.
Facebook: 'neo' has posted 'What is the Matrix?'.
Facebook: 'neo' has been logged out.