Frage Laravel4 Der Vorteil von IOC-Containern


Ich habe Schwierigkeiten, den Nutzen des IOC-Containers im Rahmen der Abhängigkeitsinjektion zu verstehen.
In Anbetracht dieses grundlegenden Beispiels:

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

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

Ich sehe den Vorteil der Verwendung von IOC-Containern gegenüber dem Erstellen einer neuen Instanz im Konstruktor nicht.
Abgesehen davon, dass ich die Vorteile getestet habe, habe ich gelesen, dass der Grund die lose Kopplung ist.
Da die "Auto" -Bindung jedoch nur eine Instanz eines neuen Autos zurückgibt, sehe ich nicht, in welchem ​​Sinne dieses Beispiel lockerer gekoppelt wäre.
Für mich scheinen die beiden genau das Gleiche zu tun.


6
2017-08-11 20:26


Ursprung


Antworten:


Sie haben Recht, in erfundenen Beispielen ist es normalerweise ein bisschen schwierig, genau zu sehen, welchen Nutzen Sie bekommen. Betrachten Sie einen realitätsnäheren Beispielcontroller:

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));
    }
}

Sehr einfach, ein Repository wird in den Konstruktor des Controllers eingefügt, der dann in der Route verwendet wird, um ein bestimmtes Auto per ID zu laden. Die Details hier im Repository sind nicht so wichtig, und vermutlich gibt es einen Dienstanbieter, der verbindlich ist CarRepositoryInterface zu einem Beton CarRepository Implementierung:

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

So ist es, jedes Mal, wenn der Controller aufgebaut wird, ein EloquentCarRepository wird erstellt und in den Konstruktor für den Controller zur Verwendung injiziert.

Aber warte, was passiert, wenn du von Eloquent zu Doctrine wechseln willst? Da wir hier die Abhängigkeitsinjektion nutzen Sie müssen keine einzelne Codezeile in Ihrem Controller ändern (oder ein anderer Controller, der Ihre aktuelle Implementierung verwendet). Alles, was Sie tun müssen, ist Ihre andere Implementierung von definieren CarRepositoryInterface, sagen, DoctrineCarRepository, und ändern Sie eine Zeile Code in Ihrem Dienstanbieter:

return new DoctrineCarRepository();

Alles andere, auf das es ankommt CarRepositoryInterface jetzt magisch funktioniert. Und all das ist dem IoC zu verdanken.

Sie können Ihrem Dienstanbieter auch komplexere Logik hinzufügen:

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

Hier das EloquentCarRepository wird nur in der Produktionsumgebung verwendet, während in anderen Umgebungen die DoctrineCarRepository wird verwendet. (Dieses Beispiel soll nur zeigen, wie Sie viel mehr Kontrolle darüber erlangen können, welche Art von Objekt zur Laufzeit erstellt wird, nicht, dass ich dafür eintrete, dies zu tun.)

Nachtrag

Wie ich in meinem Kommentar gesagt habe, ist dies die Art der Nutzung, bei der Sie nicht sicher sind, welche Art von Objekt Sie bis zur Laufzeit benötigen. Es gibt eine andere Verwendung: Dependency Management.

Angenommen, Sie haben ein Objekt, das von einem anderen Objekt abhängt:

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

Nehmen wir auch das an AnotherObject hängt von noch einem anderen Objekt ab:

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

Dies kann schnell außer Kontrolle geraten, und Sie können mit langen Abhängigkeiten aufwarten, die erfüllt sein müssen, bevor Sie das Objekt tatsächlich erstellen können. Mit dem IoC können Sie einfach eine Instanz aus dem Container herausziehen:

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

Solange der IoC alle Abhängigkeiten aufbauen kann, müssen Sie nicht wie folgt vorgehen:

$yetAnotherObj = new YetAnotherObject();
$anotherObj = new AnotherObject($yetAnotherObj);
$myObject = new MyObject($anotherObj);

5
2017-08-11 20:40



Das von Ihnen gepostete Beispiel stellt keinen echten Anwendungsfall eines IoC-Containers dar ...

Ein IoC-Container ist in diesem Beispiel nützlicher:

Wenn du eine hast BillingNotifierInterface was durch a implementiert wird EmailBillingNotifier

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

Und benutzt von a BillerInterface was durch a implementiert wird StripeBiller, so was:

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

Aber plötzlich will dein Team von wechseln EmailBillingNotifier zu SMSBillingNotifier Du änderst nur 1 Zeile und deine App funktioniert wie eine Katze ...

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

Das ist eine ECHTE CONTAINER-Anwendung ...


3
2017-08-11 20:48