You are right, in far-fetched examples it is usually difficult to understand what benefit you get. Consider an example level controller: the real world:
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, the repository is entered into the constructor of the controller, which is then used on the route to load a specific car by id. The details of the repository here are not so important and, presumably, there is a service provider linking CarRepositoryInterface with a specific implementation of CarRepository :
class RepositoryServiceProvider extends ServiceProvider { public function register() { $this->app->bind('CarRepositoryInterface', function($app) { return new EloquentCarRepository(new Car()); }); } }
So, every time a controller is created, an EloquentCarRepository is created and inserted into the constructor for use by the controller.
But wait, what happens if you want to switch from using Eloquent to say Doctrine? Since we use dependency injection here, you do not need to change one line of code in your controller (or any other controller that can use your current implementation). All you have to do is define another implementation of CarRepositoryInterface , say DoctrineCarRepository , and change one line of code from the service provider:
return new DoctrineCarRepository();
Everything else that depends on CarRepositoryInterface now works magically. And all this thanks to 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, EloquentCarRepository will only be used in the production environment, while in any other environment the DoctrineCarRepository will be used. (This example shows how you can get a lot more control over what type of object is created at runtime, rather than what I'm protecting from this.)
Adding
As I said in my comment, this is a type of use in which you are not quite sure which type of object you will need before execution. There is another use: Depedency Management.
Suppose you have an object that depends on another object:
class MyObject { public function __construct(AnotherObject $obj) { $this->obj = $obj; } }
Assume also that AnotherObject depends on another object:
class AnotherObject { public function __construct(YetAnotherObject $obj) { $this->obj = $obj; } }
This can quickly get out of hand, and you can end up with long chains of dependencies that must be satisfied before you can actually create an object. With IoC, you can simply snatch an instance from a container:
$myObject = app()->make('MyObject');
As long as IoC can create all the dependencies, you do not need to do something like this:
$yetAnotherObj = new YetAnotherObject(); $anotherObj = new AnotherObject($yetAnotherObj); $myObject = new MyObject($anotherObj);