Sök…


Introduktion

Dependency Injection (DI) är en fancy term för att "passera saker in" . Allt det egentligen innebär är att skicka ett objekts beroenden via konstruktören och / eller avläggarna istället för att skapa dem när objekt skapas inuti objektet. Dependency Injection kan också hänvisa till Dependency Injection Containers som automatiserar konstruktionen och injektionen.

Constructor Injection

Objekt beror ofta på andra objekt. Istället för att skapa beroendet i konstruktören, bör beroendet överföras till konstruktören som en parameter. Detta säkerställer att det inte finns en tät koppling mellan föremålen och gör det möjligt att ändra beroendet på klassinställning. Detta har ett antal fördelar, bland annat att göra koden lättare att läsa genom att göra beroenden tydliga, samt att testa enklare eftersom beroenden kan stängas av och hånas lättare.

I följande exempel beror Component på en instans av Logger , men den skapar inte en. Det kräver att man skickas som argument till konstruktören istället.

interface Logger {
    public function log(string $message);
}

class Component {
    private $logger;

    public function __construct(Logger $logger) {
        $this->logger = $logger;
    }
}

Utan beroendeinjicering skulle koden antagligen likna:

class Component {
    private $logger;

    public function __construct() {
        $this->logger = new FooLogger();
    }
}

Att använda new att skapa nya objekt i konstruktorn indikerar att beroendeinjektion inte användes (eller användes ofullständigt), och att koden är tätt kopplad. Det är också ett tecken på att koden är ofullständigt testad eller kan ha spröda test som gör felaktiga antaganden om programtillstånd.

I exemplet ovan, där vi istället använder beroendeinjektion, kan vi enkelt byta till en annan logger om det blev nödvändigt. Vi kan till exempel använda en Logger-implementering som loggar till en annan plats, eller som använder ett annat loggformat, eller som loggar till databasen istället för till en fil.

Setterinjektion

Beroende kan också injiceras av bosättare.

interface Logger {
    public function log($message);
}

class Component {
    private $logger;
    private $databaseConnection;

    public function __construct(DatabaseConnection $databaseConnection) {
        $this->databaseConnection = $databaseConnection;
    }

    public function setLogger(Logger $logger) {
        $this->logger = $logger;
    }

    public function core() {
        $this->logSave();    
        return $this->databaseConnection->save($this);
    }

    public function logSave() {
         if ($this->logger) {
            $this->logger->log('saving');
        }
    }
}

Detta är särskilt intressant när klassens kärnfunktionalitet inte förlitar sig på beroendet för att arbeta.

Här är det enda nödvändiga beroendet DatabaseConnection så det finns i konstruktören. Logger är valfritt och behöver därför inte ingå i konstruktören, vilket gör klassen enklare att använda.

Observera att när du använder setterinjektion är det bättre att utöka funktionaliteten snarare än att ersätta den. När du sätter ett beroende, finns det inget som bekräftar att beroendet inte kommer att förändras någon gång, vilket kan leda till oväntade resultat. Till exempel kan en FileLogger ställas in först och sedan en MailLogger kan ställas in. Detta bryter inkapslingen och gör loggar svåra att hitta, eftersom vi ersätter beroendet.

För att förhindra detta, bör vi lägga till ett beroende med setterinjektion, som så:

interface Logger {
    public function log($message);
}

class Component {
    private $loggers = array();
    private $databaseConnection;

    public function __construct(DatabaseConnection $databaseConnection) {
        $this->databaseConnection = $databaseConnection;
    }

    public function addLogger(Logger $logger) {
        $this->loggers[] = $logger;
    }

    public function core() {
        $this->logSave();
        return $this->databaseConnection->save($this);
    }

    public function logSave() {
        foreach ($this->loggers as $logger) {
            $logger->log('saving');
        }
    }
}

Så här, när vi använder kärnfunktionen kommer den inte att bryta även om det inte finns något loggerberoende, och alla loggare som lagts till kommer att användas trots att en annan logger kunde ha lagts till. Vi utökar funktionaliteten istället för att ersätta den.

Behållarinjektion

Dependency Injection (DI) i samband med att använda en Dependency Injection Container (DIC) kan ses som ett superset av konstruktörsinjektion. En DIC kommer vanligtvis att analysera en typkonstruktör av en klasskonstruktör och lösa dess behov och effektivt injicera beroenden som krävs för exekvering av instansen.

Den exakta implementeringen går långt utanför räckvidden för detta dokument, men i dess hjärta förlitar sig en DIC på att använda undervisningen av en klass ...

namespace Documentation;

class Example
{
    private $meaning;

    public function __construct(Meaning $meaning)
    {
        $this->meaning = $meaning;
    }
}

... för att automatiskt instansera det och lita mest av tiden på ett autoloaderingssystem .

// older PHP versions
$container->make('Documentation\Example');

// since PHP 5.5
$container->make(\Documentation\Example::class);

Om du använder PHP i versionen minst 5.5 och vill få ett klassnamn på ett sätt som visas ovan, är det rätta sättet det andra tillvägagångssättet. På så sätt kan du snabbt hitta användningsområden från klassen med hjälp av moderna IDE: er, vilket i hög grad kommer att hjälpa dig med potentiell refactoring. Du vill inte lita på vanliga strängar.

I det här fallet vet Documentation\Example den behöver en Meaning , och en DIC skulle i sin tur instansera en Meaning . Den konkreta implementeringen behöver inte bero på den konsumtiva instansen.

Istället sätter vi regler i containern, innan objekt skapas, som instruerar hur specifika typer ska instanseras om det behövs.

Detta har ett antal fördelar, som en DIC kan

  • Dela vanliga instanser
  • Tillhandahålla en fabrik för att lösa en typsignatur
  • Lös en gränssnittssignatur

Om vi definierar regler för hur specifik typ måste hanteras kan vi uppnå fin kontroll över vilka typer som delas, instanseras eller skapas från en fabrik.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow