
$foo = new PluginClass(1,2);
echo "\n*************\n";
echo '$foo = new PluginSubClass(1,2,3);'."\n";
$foo = new PluginSubClass(1,2,3);
echo "\n*************\n";
echo '$bar = FactoryClass::factory("PluginClass", 3,4);'."\n";
$bar = FactoryClass::factory("PluginClass", 3,4);
echo "\n*************\n";
echo '$bar = FactoryClass::factory("PluginSubClass", 3,4,5);'."\n";
$bar = FactoryClass::factory("PluginSubClass", 3,4,5);
abstract class FactoryClass {
/**
* Create an object
* @param mixed args ...
*/
function __construct() {
$args = func_get_args();
if(count($args)) {
// we've got a call with arguments
call_user_func_array(array($this, "_init"), $args);
}
}
static public function factory($name) {
$plugin = new $name();
$args = func_get_args();
array_shift($args);
// Factories often interact with plugins that do not inherit from the FactoryClass.
// Thus, _init() must be a public function.
call_user_func_array(array($plugin,"_init"), $args);
}
}
class PluginClass {
/**
* Create an object
* @param mixed args ...
*/
function __construct() {
$args = func_get_args();
if(count($args)) {
// we've got a call with arguments
call_user_func_array(array($this, "_init"), $args);
}
}
/**
* Initializer
*
* @param int $arg1 parameter 1
* @param int $arg2 parameter 2
* @param string $arg3 parameter 3
*/
public function _init($arg1, $arg2) {
// do something with $arg1, $arg2
echo "arg1=$arg1, arg2=$arg2";
}
}
class PluginSubClass extends PluginClass{
/**
* Initializer
*
* @param int $arg1 parameter 1
* @param int $arg2 parameter 2
* @param string $arg3 parameter 3
*/
public function _init($arg1, $arg2, $arg3) {
// do something with $arg1, $arg2, $arg3
echo "arg1=$arg1, arg2=$arg2, arg3=$arg3";
}
}
?>
Results:
$foo = new PluginClass(1,2);
arg1=1, arg2=2
*************
$foo = new PluginSubClass(1,2,3);
arg1=1, arg2=2, arg3=3
*************
$bar = FactoryClass::factory("PluginClass", 3,4);
arg1=3, arg2=4
*************
$bar = FactoryClass::factory("PluginSubClass", 3,4,5);
arg1=3, arg2=4, arg3=5
{code}
h2. Ugly, but Works
h3. Games with Strings
{code:title=Ugly, but works}
$pluginName = "SomePlugin";
Zend::loadClass($pluginName); # not needed if using autoload, but best practices preclude autoload here
$pluginInstance = new "Factory$pluginName"($optionsArray);
{code}
h3. Eval
Some disadvantages of {{eval}}:
# Performance
# Eval cannot benefit from bytecode caches and optimizations
# Eval is very hard to debug
{code}<?php
class Factory
{
static public function createInstance($className)
{
/**
* @todo Class name validation must go here
*/
/**
* @todo Class loading logic could go here
*/
$args = func_get_args();
array_shift($args);
if ($args === null) {
return new $className();
}
$argsCount = count($args);
$argsString = '';
$comma = false;
for ($i = 0; $i < $argsCount; $i++) {
$argsString .= ($comma ? ', ' : '') . "\$args[$i]";
$comma = true;
}
return eval("return new $className($argsString);");
}
}
class ArgsNo
{
public function __construct()
{}
}
class ArgsYes
{
public function __construct($int, $bool, $string, $array, stdClass $obj)
{}
}
class ArgsOptional
{
public function __construct($first = 1, $second = 2)
{}
}
$argsNoNew = new ArgsNo();
$argsNoFactory = Factory::createInstance('ArgsNo');
$obj = new stdClass();
$argsYesNew = new ArgsYes(1, true, 'bar', array(4, 5, 6), $obj);
$argsYesFactory = Factory::createInstance('ArgsYes', 1, true, 'bar', array(4, 5, 6), $obj);
$argsOptionalNew = new ArgsOptional();
$argsOptionalFactory = Factory::createInstance('ArgsOptional');
{code}
h1. Guidelines
{note}This section needs some improvement.{note}
{info}Guidelines are not as stringent or imperative as "best practices", and depend more intimately with the specifics of each situation.{info}
When choosing to use a factory method, the factory method should do something that either does not belong in the constructor of the instance created by the factory, or the factory method something that can not be done in that constructor.
Sometimes we have Zend_Class_Subclass1 and Zend_Class_Subclass2, and the code performing construction of both classes follow similar logic. Factories factor construction logic into the factory class. Inheritance also can help factor such construction logic into a common superclass, if all plugins can logically share a common superclass.
The facade pattern is not normally sufficient to justify the existence of a factory pattern.
Choosing between a factory and directly instantiating instances depends on many factors:
* Does the factory use logic to process inputs (e.g. settings in the registry, data provided to the constructor)?
*
* add more here
Factories can exist as standalone classes, or as static methods inside the class for which they are providing instances.
Factories can simply duplicate what could be accomplished by __construct(), or they might provide a facade to reduce the complexity of creating intances. Also, factories allow factoring shared logic out of __construct()'s, thus keeping the code of the instance "pristine" and clean of distracting code that might not directly relate to the focus of that class.
Choosing between a callback and a plugin:
*
*
*