I have a small framework and I coded it like this. I\'m not sure if it is called dependency injection or not. I don\'t know if it is like a design pattern or not. I also don\'t
You are almost there.
No, I don't see it as a valid dependency injection example. It resembles a bit a service locator (because you're injecting the entire container into your services and use it to "locate" dependent services).
You're making a small confusion between dependency injection and a dependency injection container.
First of, dependency injection means pushing dependencies into an object at runtime instead of creating/pulling them.
To exemplify this:
//hardcoded dependecies
class BadService
{
public function __construct()
{
$this->dep1 = new ConcreteObject1();
$this->dep2 = new ConcreteObject2();
}
}
So in the example above, the BadService
makes it imposible to wire other dependencies at runtime because they are already hard pulled into the constructor itself.
//service locator pattern
class AlmostGoodService
{
public function __construct(Container $container)
{
$this->dep1 = $container->getADep1();
$this->dep2 = $container->getADep2();
}
}
In the AlmostGoodService
example, we've removed the hard dependencies from the previous example but we are still depending on a specific implementation of our container (meaning that our service is not reusable without providing the implementation for that container). This is the example that matches what you're doing.
//dependecy injection
class GoodService
{
public function __construct($dep1, OptionalInterface $dep2)
{
$this->dep1 = $dep1;
$this->dep2 = $dep2;
}
}
The GoodService
service is not concerned with the creation of it's concrete dependencies and can easily be "wired" at runtime with any dependencies that implement the "protocol" of the $dep1
or the OptionalInterface for the $dep2
(therefore the name of Inversion of Control - the underlying concept behind Dependency Injection).
The component that does this wiring is called a dependency injection container.
Now, a dependency injection container, in it's simplest form, is nothing more than an object capable of wiring up your objects at runtime based on some form of configuration.
I said that you are almost there but there are some problems with your implementation:
$this
) as a dependency because then you fallback to a weaker inversion of control, namely a service locator. You should instead pass the concrete dependencies to your service constructorsThere are some cases when you'll find yourself wanting to pass the entire $container
as a dependency to a service (namely controllers or lazy service factories), but generally it will be better to stay away from this practice as it will make your services more reusable and easier to test. When you're feeling that your service has too many dependencies, then that it's a good sign that you're service does too much and it's a good time to split it.
So, based on my answers above, here is a revised (far from perfect) implementation:
/* This is the revised engine model */
class FrameWork_Engine_Model
{
function __construct($config)
{
$this->config = $cofig;
}
public function database()
{
require_once('Database.class.php');
return new Database($this->config['configParams']);
}
public function bbcode()
{
require_once('BBCode.class.php');
return new BBCode($this->database());
}
public function image()
{
require_once('Image.class.php');
$this->image = new Image($this->config['extensionName']);
}
....
public function register_controller($shared = true)
{
if ($shared && $this->register_controller) {
return $this->register_controller;
}
return $this->register_controller = new Register_Controller($this->database(), $thus->image(), $this->bbcode());
}
}
Now, to use your services:
$container = new FrameWork_Engine_Model();
$container->register_controller()->doSomeAction()
What could be improved? Your container should:
All of these are accompanied with clear documentation about Dependency Injection
FrameWork_Engine_Model
is a registry (Registry Pattern). Injecting the registry as dependency into all objects is kind of a misunderstood dependency injection. Technically it is DI, but you create a dependency from everything to everything and also take away the flexibility that DI should offer.FrameWork_Engine_Model
was meant to instantiate services and manage their dependencies, you could change it to an Inversion of Control Container (typical pattern related to DI)I won't argue your choice of class names and the responsibilities of your services and controllers, as I don't think it is within scope of this question. Just a remark: it looks like your controllers do too much. If you are interested in clean code, you might want to have a look at the Single Responsibility Principle and keep your controllers "thin", moving business logic and database queries to a service layer and output mechanisms like bbcode to views.
So, back to your example and how to change it to a sensible usage of Dependency Injection. A primitive IoC container could look like that:
public function createRegisterController()
{
$controller = new RegisterController();
$controller->setImage($this->getImageService());
// ...
return $controller;
}
public function getImageService()
{
if ($this->imageService === null) {
$this->imageService = new Image();
// inject dependencies of Image here
}
return $this->imageService;
}
The important point here is: Only inject the dependencies that are needed. And don't create a bunch of global variables disguised as DI.