Szukaj…


Wprowadzenie

Dependency Injection (DI) to fantazyjny termin na „przekazywanie rzeczy” . Wszystko, co tak naprawdę oznacza, to przekazywanie zależności obiektu za pośrednictwem konstruktora i / lub seterów zamiast tworzenia ich po utworzeniu obiektu wewnątrz obiektu. Wstrzykiwanie zależności może również odnosić się do pojemników do wstrzykiwania zależności, które automatyzują budowę i wstrzykiwanie.

Wtrysk Konstruktora

Obiekty często zależą od innych obiektów. Zamiast tworzyć zależność w konstruktorze, zależność należy przekazać do konstruktora jako parametr. To gwarantuje, że nie ma ścisłego połączenia między obiektami i umożliwia zmianę zależności po utworzeniu instancji klasy. Ma to szereg zalet, w tym ułatwienie odczytu kodu poprzez wyraźne zależności, a także uproszczenie testowania, ponieważ zależności można łatwiej zamieniać i wyśmiewać.

W poniższym przykładzie Component będzie zależał od wystąpienia programu Logger , ale go nie utworzy. Zamiast tego wymaga przekazania jednego argumentu do konstruktora.

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

class Component {
    private $logger;

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

Bez wstrzykiwania zależności kod prawdopodobnie wyglądałby podobnie do:

class Component {
    private $logger;

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

Użycie new do utworzenia nowych obiektów w konstruktorze wskazuje, że wstrzyknięcie zależności nie zostało użyte (lub zostało użyte niepełne), a kod jest ściśle powiązany. Jest to również znak, że kod jest niekompletnie przetestowany lub może mieć kruche testy, które przyjmują błędne założenia dotyczące stanu programu.

W powyższym przykładzie, w którym zamiast tego używamy wstrzykiwania zależności, moglibyśmy łatwo zmienić program rejestrujący, jeśli byłoby to konieczne. Na przykład możemy użyć implementacji Loggera, która loguje się w innej lokalizacji lub używa innego formatu rejestrowania lub loguje się do bazy danych zamiast do pliku.

Zastrzyk setera

Zależności mogą być również wprowadzane przez seterów.

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

Jest to szczególnie interesujące, gdy podstawowa funkcjonalność klasy nie polega na zależności od pracy.

Tutaj jedyną potrzebną zależnością jest DatabaseConnection więc jest w konstruktorze. Zależność programu Logger jest opcjonalna i dlatego nie musi być częścią konstruktora, dzięki czemu korzystanie z klasy jest łatwiejsze.

Pamiętaj, że w przypadku zastrzyku z ustawiaczem lepiej jest rozszerzyć funkcjonalność niż ją zastępować. Podczas ustawiania zależności nic nie potwierdza, że zależność nie zmieni się w pewnym momencie, co może prowadzić do nieoczekiwanych rezultatów. Na przykład FileLogger można ustawić FileLogger , a następnie MailLogger . Powoduje to przerwanie enkapsulacji i utrudnia znalezienie dzienników, ponieważ zastępujemy zależność.

Aby temu zapobiec, powinniśmy dodać zależność od iniekcji ustawiającej, tak jak poniżej:

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

W ten sposób, ilekroć użyjemy podstawowej funkcjonalności, nie ulegnie ona uszkodzeniu, nawet jeśli nie zostanie dodana zależność rejestratora, a każdy dodany rejestrator zostanie użyty, nawet jeśli można dodać inny rejestrator. Rozszerzamy funkcjonalność zamiast ją zastępować .

Wstrzyknięcie pojemnika

Dependency Injection (DI) w kontekście korzystania z Dependency Injection Container (DIC) można postrzegać jako nadzbiór wstrzykiwania konstruktora. DIC zazwyczaj analizuje podpowiedzi konstruktora klasy i rozwiązuje jego potrzeby, skutecznie wstrzykując zależności potrzebne do wykonania instancji.

Dokładna implementacja wykracza daleko poza zakres tego dokumentu, ale w samym sercu DIC polega na użyciu podpisu klasy ...

namespace Documentation;

class Example
{
    private $meaning;

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

... aby automatycznie utworzyć jego instancję, polegając w większości na systemie automatycznego ładowania .

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

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

Jeśli używasz PHP w wersji co najmniej 5.5 i chcesz uzyskać nazwę klasy w sposób pokazany powyżej, poprawnym sposobem jest drugie podejście. W ten sposób możesz szybko znaleźć zastosowania klasy za pomocą nowoczesnych IDE, co znacznie pomoże w potencjalnym refaktoryzacji. Nie chcesz polegać na zwykłych ciągach.

W tym przypadku Documentation\Example wie, że potrzebuje Meaning , a DIC z kolei utworzy typ Meaning . Konkretna implementacja nie musi zależeć od instancji konsumującej.

Zamiast tego ustawiamy reguły w kontenerze przed utworzeniem obiektu, które instruują, w jaki sposób należy tworzyć instancje określonych typów, jeśli zajdzie taka potrzeba.

Ma to wiele zalet, jak w przypadku DIC

  • Udostępnij typowe wystąpienia
  • Zapewnij fabrykę do rozwiązania podpisu typu
  • Rozwiąż podpis interfejsu

Jeśli zdefiniujemy zasady dotyczące sposobu zarządzania określonym typem, możemy uzyskać dokładną kontrolę nad tym, które typy są współużytkowane, tworzone lub tworzone z fabryki.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow