Hi,
I’ve been trying to incorporate the factory pattern into my current project, but I’m not sure how I should do it.
From what I have seen, there is 3 ways to utilize the factory pattern (abstract/method or something):
- As a method:
<?php
class User
{
public static function factory($id)
{
return new User(DB:factory(), $id);
}
}
?>
- One factory class
<?php
class Factory
{
public function __construct()
{
}
public function user($id)
{
return new User($this->db(), $id);
}
}
?>
- One factory class per class to instantiate:
<?php
class UserFactory extends Factory
{
public static function create($id)
{
return new User(DatabaseFactory:factory(), $id);
}
}
?>
I don’t like the first type because it hard codes the DB dependency. And the class will be directly called from my application (hard coded).
The second type will generate an enormous class (it has to have a method for all classes in the application) and if the generation of an object need other internal methods, it will be a mess.
The third type will double the number of classes in use (the number of files). The factory will be hardcoded within my code, just like the first type, however, with some abstraction.
Since you shouldn’t pass dependencies to other classes just for passing them further down in a different dependency, type 1 have to be hard coded somewhere… Type 2 doesn’t have that problem since you would naturally pass the factory if you need a dependency and that way that dependency could just be passed the same factory. Type 3 is similar to type 1 just that you hardcode the factory instead of the actual class.
I feel type 1 is out of the question since it fails with dependencies. Type 2 and 3 solves each others faults so I can’ make a choice between them…?
Would it be a viable option to do something like this:
<?php
class Factory
{
$_instances = array();
public function __construct()
{
}
public function user($id)
{
return UserFactory::create($this->db(),$id);
}
public function db()
{
return DatabaseFactory::create();
}
}
class UserFactory extends Factory
{
public static function create(Database $db, $id)
{
return new User($db, $id);
}
}
class DatabaseFactory extends Factory
{
public static function create()
{
if (isset($this->_instances['db'])) return $this->_instances['db'];
return $this->_instances['db'] = new Database;
}
}
?>
Using this method I can easily pass the main factory class to my other classes that need to create an object and no object knows about other objects…
This is a simplified outline of what I’m trying to explain (How I think it should be. Based on my option above, but also identical for type 2 above.)
<?php
$url = '/user/profile/5';
$factory = new Factory;
$request = $factory->request($url);
// $factory->request($url) internals
return new Request($this, $this->router(), $url);
// Request::__construct internals
$this->factory = $factory;
$this->current = $router->parseURL($url); // returns: array('controller' => 'user', 'action' => 'profile', 'params' => array(5));
$response = $request->exec()
// $request->exec() internals
$r = new ReflectionClass($this->current['controller']);
$controller = $r->newInstanceArgs($this->factory, $this);
// Controller constructor internals
$this->factory = $factory;
$this->request = $request;
$action = $this->current['action'];
$response = $factory->response();
$response->attach($controller->$action($params));
// Controller->action method internals
$user = $factory->user($id)
// $factory->user internals
return new UserFactory($this, $id);
// UserFactory internals
return new User($factory->DB, $id);
$view = $factory->view('user_profile');
$view->user = $user;
return $view;
return $response;
$response->sendHeaders();
echo $response;
?>
This also works great with only type 2, but with type 3, I’m not sure how I should do it…?
Using that application outline, how would you do object creation and dependency injection? And does that application outline look ok? Should I structure it differently? What do you think?
From what I’ve seen in some frameworks, they either just don’t use any factory pattern, or they use type 1 (kohana). If there is something better or more elegant, or more “oop”, please tell me
(Before anyone tells me to stop trying to reinvent the wheel and just use a well known framework and do whatever they do, please don’t. I’m trying to learn, and have enough time to do so.)
I’m sorry for this long post, and possibly the misunderstandings to come because of my not-so good English, but I hope I have made myself understood and that someone cares to give some input!
Thanks in advance!