Updated answer with sample code
Here is your new factory:
public static function factory(SimpleXMLElement $element) { $element->registerXPathNamespace("d", "http://www.w3.org/2005/Atom"); $category = $element->xpath("d:category[@scheme='http://schemas.google.com/g/2005#kind']"); $className = 'Model_Google_ '.$category[0]['label']; if (class_exists($className)){ return new $className($element); } else { throw new Exception('Cannot handle '.$category[0]['label']); } }
I'm not sure that I have exactly your point ... To rephrase the question, I realized: "How can I create the correct object without hard-coding the selection in my client code"
With autoload
So, let's start with the client base code.
class BaseFactory { public function createForType($pInformations) { switch ($pInformations['TypeOrWhatsoEver']) { case 'Type1': return $this->_createType1($pInformations); case 'Type2': return $this->_createType2($pInformations); default : throw new Exception('Cannot handle this !'); } } }
Now let's see if we can change this to avoid if / switch statuses (not always necessary, but maybe)
We will use the capabilities of PHP Autoload.
First, let's take a look at startup on site, here is our new Factory
class BaseFactory { public function createForType($pInformations) { $handlerClassName = 'GoogleDocHandler'.$pInformations['TypeOrWhatsoEver']; if (class_exists($handlerClassName)){ //class_exists will trigger the _autoload $handler = new $handlerClassName(); if ($handler instanceof InterfaceForHandlers){ $handler->configure($pInformations); return $handler; } else { throw new Exception('Handlers should implements InterfaceForHandlers'); } } else { throw new Exception('No Handlers for '.$pInformations['TypeOrWhatsoEver']); } } }
Now we have to add autoload capability
class BaseFactory { public static function autoload($className) { $path = self::BASEPATH. $className.'.php'; if (file_exists($path){ include($path); } } }
And you just need to register your autoloader, for example
spl_autoload_register(array('BaseFactory', 'autoload'));
Now, every time you have to write a new handler for types, it will be automatically added.
With chain of responsibility
You may not want to write something more “dynamic” in your factory, with a subclass that processes more than one type.
eg,
class BaseClass { public function handles($type); } class TypeAClass extends BaseClass { public function handles($type){ return $type === 'Type1'; } }
In BaseFactory code, you can load all your handlers and do something like
class BaseFactory { public function create($pInformations) { $directories = new \RegexIterator( new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator(self::BasePath) ), '/^.*\.php$/i' ); foreach ($directories as $file){ require_once($fileName->getPathName()); $handler = $this->_createHandler($file);