创建工厂,从不同的命名空间创建类

I have a question regarding the dynamic creation of classes using a factory. Let's say I have have a core namespace where a framework's base classes lie. And besides that I have a namespace for the app specific files. Now I have a factory in the cores namespace which I would use to create for example a validator class. Now there are validators which belong to the framework and validators which are application specific.

Besides that I have an autoloader which resolves the namespaces into the filesystem folders.

Example:

load from app\validators:

$passwordValidator = validatorFactory->create('PasswordValidator');

load from core\validators:

$emailValidator = validatorFactory->create('EmailValidator');

The coolest thing would be if the factory would first try to load the class from app namespace and if not possible from the core namespace. Of course I thought about implementing to the factories create method to try loading the class first from app\validators and if an exception comes up then try loading it from core\validators. But then I would have to implement all the create methods from the different factories that way and I wondered if there is a nice generic solution.

What is the best way to make the factory look in both namespaces for the requested class?

PS: I hope that question is not too silly due to I'm relatively new to such patterns.

You could rework your autoload function like this, so the error is detectable, if the file doesn't exists:

function __autoload($class) {
    $parts = explode("\\", $class);
    $path = implode(DIRECTORY_SEPARATOR, $parts) . ".php";
    if (!file_exists($path)) {
        throw new Exception("The class is not loadable");
    } else {
        include_once $path;
    }
}

And then in the factory:

class ValidatorFactory {

    public static function create($name) {
        try {
            /*
             *  try to include from the core namespace
             */
            $Class = "core\\validators\\" . $name;
            $validator = new $Class();
            return $validator;
        } catch (Exception $ex) {
            /*
             * if it's not possible, try to include from the app namespace
             * if the validator doesn't exist at all, then we
             * let the user handle the error
             */

            $Class = "app\\validators\\" . $name;
            $validator = new $Class();
            return $validator;
        }
    }
}

Then you could just use it like this:

$myValidator = ValidatorFactory::create("EmailValidator");

In your context, i think it would be more suitable to use a static create(), but maybe i'm wrong.

You could also document the create() methods return value, so that your IDE (netbeans, eclipse, whatever) will know the return type, and show suggestions (I suppose there is a base interface or abstract class in your library for the validators):

/**
 * @return \core\validators\IValidator 
 */
public static function create($name) {
    ...
}

EDIT:

You could use something like this, but you would need to create factory instances, which isn't the best idea in my opinion, but here you go:

Abstract base class, with a concrete implementation of create()

abstract class AbstractFactory {

    private $namespaces = array();

    public function __construct($namespaces) {
        $this->namespaces = $namespaces;
    }

    public function create($name) {
        foreach ($this->namespaces as $namespace) {
            $Class = $namespace . "\\" . $name;
            try {
                $instance = new $Class();
                return $instance;
            } catch (Exception $ex) {
                // empty
            }
        }
        throw new Exception("No class found");
    }

}

An implementation

class ValidatorFactory extends AbstractFactory {

    public function __construct() {
        parent::__construct(array(
            "app\\validators",
            "core\\validators"
        ));
    }
}

And then:

$factory = new ValidatorFactory();
$myValidator = $factory->create("MyValidator");