Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it dependency injection and is it a bad practice?

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 know and wonder if passing $this as param is a bad practice.

Have a look at this; (Not a working example, just wrote those codes into browser for explanation.)

/* This is engine model */
require_once('Database.class.php');
require_once('Image.class.php');
require_once('Misc.class.php');
require_once('BBCode.class.php');

class FrameWork_Engine_Model
{
    public $database, $config, $misc, $bbcode, $controller, $image;

    function __construct($config)
    {
            $this->database = new Database($configParams);
            $this->image = new Image($this);
            $this->misc = new Misc($this);
            $this->bbcode = new BBCode($this);
            $this->controller = new Controller($this); //here I call Register controller depending on routing, in this case, register controller.
    }
 ...
 }

 /* This is register controller */
 class Register extends Base_Controller
 {
       /*I can access anything over Engine Model in my controllers */
       $this->engine->database->query(); //I access database model
       $this->engine->bbcode->tag('you'); //I access bbcode model
       $this->engine->image->sanitizeUploadedFile(); //I access image model

       //etc. I can access others models like this.
 }

Basically, my controllers can access any models via engine model. I believe dependency injection is all about injecting dependencies into controllers? Like, my register controller needs a database model, routing model, and templating model to work. Here it has everything it depends on. Am I mistaken?

With those said, my questions are:

  1. Is it a valid dependency injection example? If not, what it is? Does it have a name in design patterns?

  2. If it is nothing related to dependency injection, what changes needs to be done to be DI?

  3. Is passing $this parameter on newly created classes is a bad practise? If so, why?

Ps. I know asking 3 questions in a topic isn't something stackoverflow likes, but I don't want to copy paste entire text to ask them.

like image 931
Lisa Miskovsky Avatar asked Mar 10 '13 15:03

Lisa Miskovsky


People also ask

Is dependency injection a good practice?

Yes, dependency injection is always a best practice. I say that having worked in . NET over a period of 10 years; some projects had heavy DI and some had none and some had service locators.

Is dependency injection an overkill?

If you have a really small project with 12 classes, then a DI framework is almost certainly overkill. As a rule of thumb, the point where it becomes truly useful is when you find yourself repeatedly writing code that wires up object graphs with multiple dependencies and have to think about where to put that code.

What is the disadvantage of dependency injection?

The main drawback of dependency injection is that using many instances together can become a very difficult if there are too many instances and many dependencies that need to be resolved.

Should every dependency be injected?

My answer is no, you do not have to do that use dependency injection every time you need to create an object.


1 Answers

You are almost there.

Question 1

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).

Question 2

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:

  • the wiring should be lazy (you do not want to make all that work in your constructor, as your application would slow down considerably as it grows)
  • you should not pass the entire container ($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 constructors

Question 3

There 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.

Prototype Container Implementation

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:

  • provide a way to share services - that is, to initialise them only once
  • be lockable - provide a way to lock it after configuration
  • be able to "merge" with other containers - that way your application will be really modular
  • allow optional dependencies
  • allow scopes
  • support tagging services

Ready to use DI Container Implementations

All of these are accompanied with clear documentation about Dependency Injection

  • Pimple - PHP 5.3 lightweight DI container
  • Symfony2 DI Container - PHP 5.3 feature full DI container
  • Juice DI - Small PHP 5.2 DI container
like image 80
catalin.costache Avatar answered Oct 14 '22 03:10

catalin.costache