useful php patterns stencil means hidden to the eye
this post is part of the practical php pattern series.
the pattern of today is the template method one.factoring business template method is an inheritance solution to the problem of hooking into steps of the execution of an algorithm.
the pattern is very simple: the template method on an abstractclass defines the algorithm by composing small hook methods, which can be implemented or overridden by different concreteclass that inherit from abstractclass. the abstractclass may provide the hooks as abstract methods,factoring business or as concrete methods with default implementations; in php their visibility is usually protected so that they are visible only to the hierarchy internal code.
an example of template method where a standard hook is used is the factory method pattern: businessmethod() is a template method which composes factorymethod() as an hook.
from the design and testability point of view, template method should be used sparingly,factoring business and mostly in high-level components which acts more as a declarative layer and are not the subject of extensive unit testing. this means they should not contain much real logic.
in fact, the testing of abstractclass is usually non-standard for programmers that has made an habit of tdd, but it is indeed possible: a custom subclass of abstractclass is built specifically for the test,factoring business or a mock is generated which overrides only the hook methods. testing the single concreteclasses is instead difficult as in every test you will throw in also the business logic of abstractclass, which was factored out specifically to avoid dealing with it.
you can lie to the production code by factoring out business logic with an extends keyword and hiding it under the carpet, but you can't lie to unit tests that exercise this production code.factoring business so if you find yourself struggling with testing concreteclass instances in isolation, you may want to refactor into a strategy pattern or a similar composition solution built with dependency injection in mind. the particular pattern depends on the semantics of your object graph.
the code sample deals with multiple implementations of binary operations,factoring business which only define the sources of the operands and the business logic of the actual operation, sharing the wiring code.
/**
factoring business
* the abstractclass.
*/
abstract class binaryoperation
{
/**
factoring business
* these are three hooks defined, which should
* provide the two numbers which the operation is
* applied to and its business logic.
*/
factoring business
protected abstract function _getfirstnumber();
protected abstract function _getsecondnumber();
protected abstract function _operator($a, $b);
/**
factoring business
* this is the template method.
* it uses all the three hooks, but a typical
* template method can coexist with other ones, and
factoring business
* share hooks with them.
* @return numeric
*/
public function getoperationresult()
{
factoring business
$a = $this->_getfirstnumber();
$b = $this->_getsecondnumber();
return $this->_operator($a, $b);
}
}
factoring business
/**
* a concreteclass.
*/
class sum extends binaryoperation
factoring business
{
private $_a;
private $_b;
public function __construct($a = 0, $b = 0)
factoring business
{
$this->_a = $a;
$this->_b = $b;
}
protected function _getfirstnumber()
factoring business
{
return $this->_a;
}
protected function _getsecondnumber()
{
factoring business
return $this->_b;
}
protected function _operator($a, $b)
{
factoring business
return $a + $b;
}
}
/**
* a concreteclass.factoring business
*/
class nonnegativesubtraction extends binaryoperation
{
private $_a;
private $_b;
factoring business
public function __construct($a = 0, $b = 0)
{
$this->_a = $a;
$this->_b = $b;
factoring business
}
protected function _getfirstnumber()
{
return $this->_a;
}
factoring business
protected function _getsecondnumber()
{
return min($this->_a, $this->_b);
}
factoring business
protected function _operator($a, $b)
{
return $a - $b;
}
factoring business
}
// client code
$sum = new sum(84, 56);
factoring business
echo $sum->getoperationresult(), "\n";
$nonnegativesubtraction = new nonnegativesubtraction(9, 14);
echo $nonnegativesubtraction->getoperationresult(), "\n";