Обновленный ответ с вашим примером кода
Вот ваш новый завод:
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']);
}
}
Я не уверен, что понял вашу точку зрения ... Чтобы перефразировать вопрос, я понял, "как я могу создать правильный объект, не кодируя выделение в моем коде клиента"
с автозагрузкой
Итак, начнем с базового клиентского кода
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 !');
}
}
}
Теперь давайте посмотрим, сможем ли мы изменить это, чтобы избежать установки if / switch (не всегда необходимо, но может быть)
Мы собираемся использовать возможности автозагрузки PHP.
Во-первых, рассмотрим автозагрузку, вот наш новый Фабрика
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']);
}
}
}
Теперь мы должны добавить возможность автозагрузки
class BaseFactory
{
public static function autoload($className)
{
$path = self::BASEPATH.
$className.'.php';
if (file_exists($path){
include($path);
}
}
}
А вам просто нужно зарегистрировать свой автозагрузчик как
spl_autoload_register(array('BaseFactory', 'autoload'));
Теперь каждый раз, когда вам нужно будет написать новый обработчик для типов, он будет автоматически добавлен.
С цепочкой ответственности
Возможно, вы не захотите писать что-то более «динамичное» на своей фабрике с подклассом, который обрабатывает более одного типа.
например
class BaseClass
{
public function handles($type);
}
class TypeAClass extends BaseClass
{
public function handles($type){
return $type === 'Type1';
}
}
//....
В коде BaseFactory вы можете загрузить все свои обработчики и сделать что-то вроде
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);//gets the classname and create it
if ($handler->handles($pInformations['type'])){
return $handler;
}
}
throw new Exception('No Handlers for '.$pInformations['TypeOrWhatsoEver']);
}
}