Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel4 The benefit of IOC container

I have trouble understanding the benefit of the IOC container in the scope of dependency injection.
Considering this basic example:

App::bind('Car', function()
{
    return new Car;
});

Route::get('/', function()
{
    dd(App::make('Car'));  //  resolve it
}); 

I don't see the benefit of using IOC container over creating a new instance in the constructor.
Aside from testing benefits, I read that the reason is loose coupling.
However, as the 'car' binding just returns an instance of a new car, I don't see in which sense this example would be more loosely coupled.
For me, the two appear to be doing exactly the same thing.

like image 475
html_programmer Avatar asked Aug 11 '14 20:08

html_programmer


People also ask

What are the benefits of IoC in Spring?

The benefits of inversion of control in Spring and Java are a developer can maintain the creation, configuration, provisioning and lifecycle of all container-managed objects separately from the code where they are referenced. As such, IoC eases the software developer's concern about these aforementioned activities.

What does IoC container mean?

inversion of control (IoC)

What is the use of IoC container in C#?

IoC means that one code calls another; DI goes beyond that and implements IoC by using composition. A DI or IoC container needs to instantiate objects (dependencies) and provide them to the application. To do so, it must deal with constructor injection, setter injection, and interface injection.


2 Answers

You're right, in contrived examples its usually a bit difficult to see exactly what benefit you're getting. Consider a closer-to-real-world example controller:

class TestController
{
    public function __construct(CarRepositoryInterface $car)
    {
        $this->_repository = $car;
    }

    public function route($id)
    {
        return View::make('my.view')->with('car', $this->_repository->find($id));
    }
}

Very simple, a repository is being injected into the controller's constructor which is then being used in the route to load a specific car by id. The details here of the repository aren't all that important, and presumably there's a service provider that's binding CarRepositoryInterface to a concrete CarRepository implementation:

class RepositoryServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->bind('CarRepositoryInterface', function($app) {
            return new EloquentCarRepository(new Car());
        });
    }
}

So there it is, everytime the controller gets constructed, an EloquentCarRepository gets created and injected into the constructor for the controller to use.

But wait, what happens if you want to switch from using Eloquent to say, Doctrine? Since we're leveraging dependency injection here you don't have to change a single line of code in your controller (or any other controller that may be using your current implementation). All you have to do is define your other implementation of CarRepositoryInterface, say, DoctrineCarRepository, and change one line of code in your service provider:

return new DoctrineCarRepository();

Everything else that depends on CarRepositoryInterface now magically works. And all of this is thanks to the IoC.

You can also add more complex logic to your service provider:

public function register()
{
    $this->app->bind('CarRepositoryInterface', function($app) {
        if($app->environment() == 'production') {
            return new EloquentCarRepository(new Car());
        } else {
            return new DoctrineCarRepository(new Car());
        }
    });
}

Here the EloquentCarRepository will be used only in the production environment, whereas on any other environment the DoctrineCarRepository will be used. (This example is only to show how you can gain a lot more control over what type of object gets constructed at runtime, not that I'm advocating actually doing this..)

Addendum

As I stated in my comment, this is the type of usage where you're not quite sure what type of object you're going to need until runtime. There is another usage: Depedency Management.

Suppose you have an object that depends on another object:

class MyObject
{
    public function __construct(AnotherObject $obj)
    {
        $this->obj = $obj;
    }
}

Suppose also that AnotherObject depends on yet another object:

class AnotherObject
{
    public function __construct(YetAnotherObject $obj)
    {
        $this->obj = $obj;
    }
}

This can quickly spiral out of control, and you can wind up with long chains of dependencies that need to be satisfied before you can actually create the object. With the IoC, you can just pluck an instance out of the container:

$myObject = app()->make('MyObject');

As long as the IoC can construct all of the dependencies, you don't have to do something like this:

$yetAnotherObj = new YetAnotherObject();
$anotherObj = new AnotherObject($yetAnotherObj);
$myObject = new MyObject($anotherObj);
like image 117
Jeff Lambert Avatar answered Sep 18 '22 23:09

Jeff Lambert


That example you post, doesn't represent a real use case of an IoC Container...

An IoC Container its more usefull in this example:

When you have a BillingNotifierInterface which is implemented by a EmailBillingNotifier

App::bind('BillingNotifierInterface', function()
{
  return new EmailBillingNotifier;
});

And is using by a BillerInterface which is implemented by a StripeBiller, like this:

App::bind('BillerInterface', function()
{
  return new StripeBiller(App::make('BillingNotifierInterface'));
})

But suddenly your team want to change from EmailBillingNotifier to SMSBillingNotifier you just change 1 line and your app keep working smooth as a cat...

App::bind('BillingNotifierInterface', function()
{
  return new SMSBillingNotifier;
});

That is a REAL IoC CONTAINER application...

like image 28
NBPalomino Avatar answered Sep 21 '22 23:09

NBPalomino