Ricerca…


introduzione

Dipendenza iniezione (DI) è un termine di fantasia per "passare le cose in" . Tutto ciò che significa veramente è passare le dipendenze di un oggetto tramite il costruttore e / o setter invece di crearli dopo la creazione dell'oggetto all'interno dell'oggetto. L'iniezione di dipendenza potrebbe anche riferirsi a contenitori di iniezione di dipendenza che automatizzano la costruzione e l'iniezione.

Costruttore di iniezione

Gli oggetti dipenderanno spesso da altri oggetti. Invece di creare la dipendenza nel costruttore, la dipendenza deve essere passata al costruttore come parametro. Ciò garantisce che non vi sia un accoppiamento stretto tra gli oggetti e consente di modificare la dipendenza dall'istanza della classe. Questo ha un certo numero di vantaggi, tra cui rendere il codice più facile da leggere rendendo esplicite le dipendenze, oltre a semplificare i test in quanto le dipendenze possono essere sostituite e derise più facilmente.

Nell'esempio seguente, Component dipenderà da un'istanza di Logger , ma non ne creerà uno. Richiede invece di passarlo come argomento al costruttore.

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

class Component {
    private $logger;

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

Senza l'iniezione di dipendenza, il codice sarebbe probabilmente simile a:

class Component {
    private $logger;

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

L'utilizzo di new per creare nuovi oggetti nel costruttore indica che l'iniezione di dipendenza non è stata utilizzata (o è stata utilizzata in modo incompleto) e che il codice è strettamente accoppiato. È anche un segno che il codice è stato testato in modo incompleto o potrebbe avere test fragili che fanno ipotesi errate sullo stato del programma.

Nell'esempio precedente, dove invece stiamo usando l'iniezione di dipendenze, potremmo facilmente passare a un altro Logger se ciò fosse necessario. Ad esempio, potremmo utilizzare un'implementazione di Logger che registra in una posizione diversa o che utilizza un formato di registrazione diverso o che registra nel database anziché in un file.

Iniezione Setter

Le dipendenze possono anche essere iniettate dai setter.

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

Ciò è particolarmente interessante quando le funzionalità di base della classe non si basano sulla dipendenza dal lavoro.

Qui, l' unica dipendenza necessaria è DatabaseConnection quindi è nel costruttore. La dipendenza di Logger è facoltativa e quindi non ha bisogno di essere parte del costruttore, rendendo la classe più facile da usare.

Notare che quando si usa l'iniezione setter, è meglio estendere la funzionalità piuttosto che sostituirla. Quando si imposta una dipendenza, non c'è nulla che confermi che la dipendenza non cambierà a un certo punto, il che potrebbe portare a risultati imprevisti. Ad esempio, un FileLogger può essere impostato in un primo momento e quindi è possibile impostare un MailLogger . Questo rompe l'incapsulamento e rende difficile trovare i registri, perché stiamo sostituendo la dipendenza.

Per evitare ciò, dovremmo aggiungere una dipendenza con l'iniezione setter, in questo modo:

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

In questo modo, ogni volta che useremo la funzionalità di base, non si romperà nemmeno se non è stata aggiunta alcuna dipendenza del logger e verrà utilizzato qualsiasi logger aggiunto anche se potrebbe essere stato aggiunto un altro logger. Stiamo estendendo la funzionalità anziché sostituirla .

Iniezione del contenitore

Dipendenza dell'iniezione (DI) nel contesto dell'utilizzo di un contenitore per iniezione di dipendenza (DIC) può essere visto come un superset dell'iniezione del costruttore. In genere un DIC analizza i tipi di un costruttore di classi e risolve i suoi bisogni, iniettando in modo efficace le dipendenze necessarie per l'esecuzione dell'istanza.

L'implementazione esatta va ben oltre lo scopo di questo documento ma, in fondo, una DIC si basa sull'uso della firma di una classe ...

namespace Documentation;

class Example
{
    private $meaning;

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

... per istanziarlo automaticamente, affidandosi quasi sempre a un sistema di caricamento automatico .

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

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

Se stai usando PHP in versione almeno 5.5 e vuoi ottenere un nome di una classe in un modo che viene mostrato sopra, il modo corretto è il secondo approccio. In questo modo puoi rapidamente trovare gli usi della classe utilizzando i moderni IDE, che ti aiuteranno molto con un potenziale refactoring. Non vuoi fare affidamento su stringhe regolari.

In questo caso, Documentation\Example sa che ha bisogno di un Meaning , e un DIC a sua volta istanzia un tipo di Meaning . L'implementazione concreta non deve dipendere dall'istanza che consuma.

Invece, impostiamo le regole nel contenitore, prima della creazione dell'oggetto, che indica come devono essere istanziati tipi specifici se necessario.

Questo ha un certo numero di vantaggi, come può fare un DIC

  • Condividi istanze comuni
  • Fornire una fabbrica per risolvere una firma di tipo
  • Risolvi una firma dell'interfaccia

Se definiamo regole su come deve essere gestito il tipo specifico, possiamo ottenere un controllo preciso su quali tipi sono condivisi, istanziati o creati da una fabbrica.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow