Zostało już opublikowanych kilka pytań z konkretnymi pytaniami dotyczącymi wstrzykiwania zależności , na przykład kiedy z niego korzystać i jakie są dostępne ramy. Jednak,
Co to jest zastrzyk zależności i kiedy / dlaczego należy go stosować lub nie?
Zostało już opublikowanych kilka pytań z konkretnymi pytaniami dotyczącymi wstrzykiwania zależności , na przykład kiedy z niego korzystać i jakie są dostępne ramy. Jednak,
Co to jest zastrzyk zależności i kiedy / dlaczego należy go stosować lub nie?
Odpowiedzi:
Dependency Injection przekazuje zależność do innych obiektów lub frameworka (aplikator zależności).
Wstrzykiwanie zależności ułatwia testowanie. Wstrzyknięcie można wykonać za pomocą konstruktora .
SomeClass() ma następujący konstruktor:
public SomeClass() {
myObject = Factory.getObject();
}
Problem : W przypadku myObjectzłożonych zadań, takich jak dostęp do dysku lub dostęp do sieci, trudno jest przeprowadzić test jednostkowy SomeClass(). Programiści muszą kpić myObjecti mogą przechwycić połączenie fabryczne.
Alternatywne rozwiązanie :
myObjectjako argument do konstruktorapublic SomeClass (MyClass myObject) {
this.myObject = myObject;
}
myObject można przekazać bezpośrednio, co ułatwia testowanie.
Trudniej jest izolować komponenty w testach jednostkowych bez wstrzykiwania zależności.
Kiedy w 2013 roku napisałem tę odpowiedź, był to główny temat na blogu testowym Google . Pozostaje to dla mnie największą zaletą, ponieważ programiści nie zawsze potrzebują dodatkowej elastyczności przy projektowaniu w czasie wykonywania (na przykład dla lokalizatora usług lub podobnych wzorców). Programiści często muszą izolować klasy podczas testowania.
Najlepsza definicja, jaką do tej pory znalazłem, to James Shore :
„Wstrzykiwanie zależności” to termin 25 dolarów za koncepcję 5 centów. [...] Wstrzykiwanie zależności oznacza nadanie obiektowi zmiennych instancji. [...]
Jest też artykuł Martina Fowlera, który może okazać się przydatny.
Wstrzykiwanie zależności polega w zasadzie na dostarczeniu obiektów, których potrzebuje obiekt (jego zależności), zamiast samemu je konstruować. Jest to bardzo przydatna technika testowania, ponieważ pozwala na kpienie lub eliminowanie zależności.
Zależności można wstrzykiwać do obiektów na wiele sposobów (takich jak wstrzykiwanie konstruktora lub wstrzykiwanie setera). W tym celu można nawet użyć specjalistycznych struktur wstrzykiwania zależności (np. Spring), ale z pewnością nie są one wymagane. Te frameworki nie są potrzebne do wstrzyknięcia zależności. Bezpośrednie tworzenie instancji i przekazywanie obiektów (zależności) jest tak samo dobre jak wstrzyknięcie jak wstrzyknięcie przez framework.
Znalazłem ten zabawny przykład w kategoriach luźnego sprzężenia :
Każda aplikacja składa się z wielu obiektów, które współpracują ze sobą w celu wykonania użytecznych czynności. Tradycyjnie każdy obiekt jest odpowiedzialny za uzyskanie własnych odniesień do obiektów zależnych (zależności), z którymi współpracuje. Prowadzi to do wysoce sprzężonych klas i trudnego do przetestowania kodu.
Na przykład rozważmy Car przedmiot.
A Carzależy od kół, silnika, paliwa, akumulatora itp. Tradycyjnie definiujemy markę takich zależnych obiektów wraz z definicjąCar obiektu.
Bez wtrysku zależności (DI):
class Car{
private Wheel wh = new NepaliRubberWheel();
private Battery bt = new ExcideBattery();
//The rest
}
Tutaj Carobiekt jest odpowiedzialny za tworzenie obiektów zależnych.
Co jeśli chcemy zmienić typ obiektu zależnego - powiedzmy Wheel- po początkowych NepaliRubberWheel()nakłuciach? Musimy odtworzyć obiekt Car z jego nową zależnością ChineseRubberWheel(), ale tylko Carproducent może to zrobić.
Co zatem Dependency Injectiondla nas robi ...?
Podczas stosowania wstrzykiwania zależności obiekty otrzymują swoje zależności w czasie wykonywania, a nie w czasie kompilacji (czas produkcji samochodu) . Abyśmy mogli teraz zmieniać, Wheelkiedy tylko chcemy. Tutaj można wstrzyknąć dependency( wheel)Car w czasie wykonywania.
Po zastosowaniu wstrzyknięcia zależności:
Tutaj jesteśmy wstrzykiwanie z zależnościami (koło i akumulator) przy starcie. Stąd termin: wstrzyknięcie zależności.
class Car{
private Wheel wh; // Inject an Instance of Wheel (dependency of car) at runtime
private Battery bt; // Inject an Instance of Battery (dependency of car) at runtime
Car(Wheel wh,Battery bt) {
this.wh = wh;
this.bt = bt;
}
//Or we can have setters
void setWheel(Wheel wh) {
this.wh = wh;
}
}
newdo opona? Ja nie. Wszystko, co muszę zrobić, to kupić od nich (wstrzyknąć przez param), zainstalować i wah-lah! Tak więc, wracając do programowania, powiedzmy, że projekt C # musi korzystać z istniejącej biblioteki / klasy, istnieją dwa sposoby uruchomienia / debugowania, 1-dodawanie odwołania do całego projektu
newniego, opcja 2 przekazuje ją jako parametr. Może nie być dokładne, ale proste, głupie, łatwe do zrozumienia.
Wstrzykiwanie zależności to praktyka polegająca na tym, że obiekty są projektowane w taki sposób, że otrzymują instancje obiektów z innych fragmentów kodu zamiast konstruować je wewnętrznie. Oznacza to, że każdy obiekt implementujący interfejs, który jest wymagany przez obiekt, można podstawić bez zmiany kodu, co upraszcza testowanie i poprawia odsprzęganie.
Rozważmy na przykład następujące klauzule:
public class PersonService {
public void addManager( Person employee, Person newManager ) { ... }
public void removeManager( Person employee, Person oldManager ) { ... }
public Group getGroupByManager( Person manager ) { ... }
}
public class GroupMembershipService() {
public void addPersonToGroup( Person person, Group group ) { ... }
public void removePersonFromGroup( Person person, Group group ) { ... }
}
W tym przykładzie wdrożenie PersonService::addManageri PersonService::removeManagerpotrzebowałoby wystąpienia instancji GroupMembershipServicew celu wykonania swojej pracy. Bez wstrzykiwania zależności tradycyjnym sposobem wykonania tego byłoby utworzenie nowego elementu GroupMembershipServicew konstruktorze PersonServicei użycie tego atrybutu instancji w obu funkcjach. Jednakże, jeśli konstruktor GroupMembershipServicema wiele rzeczy, których wymaga, lub jeszcze gorzej, istnieją pewne „ustawiacze” inicjujące, które należy wywołać w GroupMembershipService, kod rośnie dość szybko, a PersonServiceteraz zależy nie tylko od, GroupMembershipServiceale i wszystkiego innego, co GroupMembershipServicezależy od. Co więcej, powiązanie z GroupMembershipServicejest zakodowane na stałe, PersonServiceco oznacza, że nie można „zrobić manekina”GroupMembershipService do celów testowych lub do wykorzystania wzorca strategii w różnych częściach aplikacji.
Dzięki Dependency Injection zamiast tworzenia instancji GroupMembershipServicewewnątrz PersonService, możesz przekazać ją PersonServicekonstruktorowi lub dodać właściwość (moduł pobierający i ustawiający), aby ustawić jego lokalną instancję. Oznacza to, że PersonServicenie musisz się już martwić, jak utworzyć GroupMembershipService, akceptuje tylko te, które otrzymałeś i współpracuje z nimi. Oznacza to również, że wszystko, co jest podklasą GroupMembershipServicelub implementacją GroupMembershipServiceinterfejsu, może zostać „wstrzyknięte” do PersonServicei PersonServicenie musi wiedzieć o zmianie.
Przyjęta odpowiedź jest dobra - ale chciałbym dodać do tego, że DI jest bardzo podobny do klasycznego unikania stałych zakodowanych w kodzie.
Kiedy używasz stałej, np. Nazwy bazy danych, szybko przenosisz ją z wnętrza kodu do jakiegoś pliku konfiguracyjnego i przekazujesz zmienną zawierającą tę wartość do miejsca, w którym jest potrzebna. Powodem tego jest to, że te stałe zwykle zmieniają się częściej niż reszta kodu. Na przykład, jeśli chcesz przetestować kod w testowej bazie danych.
DI jest analogiczne do tego w świecie programowania obiektowego. Wartości zamiast stałych literałów są całymi obiektami - ale powód przeniesienia tworzącego je kodu z kodu klasy jest podobny - obiekty zmieniają się częściej niż kod, który ich używa. Jednym z ważnych przypadków, w których taka zmiana jest potrzebna, są testy.
Spróbujmy prostego przykładu z klasami samochodów i silników , każdy samochód potrzebuje silnika, aby pojechać gdziekolwiek, przynajmniej na razie. Poniżej poniżej, jak będzie wyglądał kod bez wstrzykiwania zależności.
public class Car
{
public Car()
{
GasEngine engine = new GasEngine();
engine.Start();
}
}
public class GasEngine
{
public void Start()
{
Console.WriteLine("I use gas as my fuel!");
}
}
Aby utworzyć instancję klasy Car, użyjemy następnego kodu:
Car car = new Car();
Problem z tym kodem, który ściśle powiązaliśmy z GasEngine i jeśli zdecydujemy się go zmienić na ElectricityEngine, będziemy musieli przepisać klasę samochodu. Im większa aplikacja, tym więcej problemów i bólu głowy będziemy musieli dodać i użyć nowego typu silnika.
Innymi słowy, przy takim podejściu nasza klasa wysokiego poziomu zależy od klasy GasEngine niższego poziomu, która narusza zasadę inwersji zależności (DIP) od SOLID. DIP sugeruje, że powinniśmy polegać na abstrakcjach, a nie na konkretnych klasach. Aby to spełnić, wprowadzamy interfejs IEngine i przepisujemy kod jak poniżej:
public interface IEngine
{
void Start();
}
public class GasEngine : IEngine
{
public void Start()
{
Console.WriteLine("I use gas as my fuel!");
}
}
public class ElectricityEngine : IEngine
{
public void Start()
{
Console.WriteLine("I am electrocar");
}
}
public class Car
{
private readonly IEngine _engine;
public Car(IEngine engine)
{
_engine = engine;
}
public void Run()
{
_engine.Start();
}
}
Teraz nasza klasa samochodów jest zależna tylko od interfejsu IEngine, a nie od konkretnej implementacji silnika. Teraz jedyną sztuczką jest to, jak stworzyć instancję samochodu i nadać jej konkretną konkretną klasę silnika, taką jak GasEngine lub ElectricityEngine. Tam właśnie wchodzi Dependency Injection .
Car gasCar = new Car(new GasEngine());
gasCar.Run();
Car electroCar = new Car(new ElectricityEngine());
electroCar.Run();
Tutaj w zasadzie wstrzykujemy (przekazujemy) naszą zależność (instancję silnika) do konstruktora Car. Więc teraz nasze klasy mają luźne sprzężenie między obiektami i ich zależnościami i możemy łatwo dodawać nowe typy silników bez zmiany klasy samochodu.
Główną zaletą wstrzykiwania zależności jest to, że klasy są luźniej sprzężone, ponieważ nie mają sztywnych zależności. Jest to zgodne z zasadą inwersji zależności, o której wspomniano powyżej. Zamiast odwoływać się do konkretnych implementacji, klasy żądają abstrakcji (zwykle interfejsów ), które są im dostarczane, kiedy klasa jest konstruowana.
Tak więc w końcu Wstrzykiwanie zależności jest tylko techniką osiągania luźnego sprzężenia między obiektami i ich zależnościami. Zamiast bezpośrednio tworzyć instancje zależności, których klasa potrzebuje do wykonania swoich działań, zależności są dostarczane do klasy (najczęściej) poprzez wstrzyknięcie konstruktora.
Również gdy mamy wiele zależności, bardzo dobrą praktyką jest używanie kontenerów Inversion of Control (IoC), dzięki którym możemy stwierdzić, które interfejsy powinny być mapowane na konkretne implementacje dla wszystkich naszych zależności, i możemy sprawić, aby te zależności zostały dla nas rozwiązane podczas konstruowania nasz przedmiot. Na przykład możemy określić w mapowaniu kontenera IoC, że zależność IEngine powinna być mapowana na klasę GasEngine, a kiedy poprosimy kontener IoC o instancję naszej klasy Car , automatycznie skonstruuje naszą klasę Car z zależnością GasEngine przeszedł.
AKTUALIZACJA: Niedawno obejrzałem kurs na temat EF Core autorstwa Julie Lerman, a także polubiłem jej krótką definicję dotyczącą DI.
Wstrzykiwanie zależności to wzorzec umożliwiający aplikacji wstrzykiwanie obiektów w locie do klas, które ich potrzebują, bez zmuszania tych klas do odpowiedzialności za te obiekty. Pozwala to na luźniejsze sprzężenie kodu, a Entity Framework Core podłącza się do tego samego systemu usług.
Wyobraźmy sobie, że chcesz łowić ryby:
Bez zastrzyku uzależnienia musisz sam wszystko załatwić. Musisz znaleźć łódź, kupić wędkę, poszukać przynęty itp. Oczywiście jest to możliwe, ale wiąże się to z dużą odpowiedzialnością. Pod względem oprogramowania oznacza to, że musisz wykonać wyszukiwanie wszystkich tych rzeczy.
Dzięki wstrzyknięciu zależności ktoś zajmuje się całym przygotowaniem i udostępnia wymagane wyposażenie. Otrzymasz („zastrzyk”) łódź, wędkę i przynętę - wszystko gotowe do użycia.
To jest najprostsze wyjaśnienie dotyczące wstrzykiwania zależności i kontenera wstrzykiwania zależności , jakie kiedykolwiek widziałem:
Wstrzykiwanie zależności i zależność Pojemniki do wstrzykiwań są różne:
Nie potrzebujesz pojemnika do wstrzykiwania zależności. Jednak pojemnik może ci pomóc.
Czy „wstrzykiwanie zależności” nie oznacza po prostu używania sparametryzowanych konstruktorów i ustawiaczy publicznych?
Artykuł Jamesa Shore'a pokazuje następujące przykłady do porównania .
Konstruktor bez wstrzykiwania zależności:
public class Example { private DatabaseThingie myDatabase; public Example() { myDatabase = new DatabaseThingie(); } public void doStuff() { ... myDatabase.getData(); ... } }Konstruktor z wtryskiem zależności:
public class Example { private DatabaseThingie myDatabase; public Example(DatabaseThingie useThisDatabaseInstead) { myDatabase = useThisDatabaseInstead; } public void doStuff() { ... myDatabase.getData(); ... } }
new DatabaseThingie()nie wygeneruje prawidłowej instancji myDatabase.
Aby uczynić koncepcję wtrysku zależnego prostą do zrozumienia. Weźmy przykład przycisku przełącznika do przełączania (włączania / wyłączania) żarówki.
Switch musi wcześniej wiedzieć, do której żarówki jestem podłączony (zależność na stałe). Więc,
Przełącznik -> Przełącznik PermanentBulb // jest bezpośrednio podłączony do stałej żarówki, testowanie nie jest łatwe
Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}
Switch wie tylko, że muszę włączyć / wyłączyć cokolwiek, co zostanie mi przekazane. Więc,
Przełącznik -> Żarówka 1 LUB Żarówka 2 LUB NightBulb (zależność wstrzyknięta)
Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}
Modyfikacja Jamesa dla przełącznika i żarówki:
public class SwitchTest {
TestToggleBulb() {
MockBulb mockbulb = new MockBulb();
// MockBulb is a subclass of Bulb, so we can
// "inject" it here:
Switch switch = new Switch(mockBulb);
switch.ToggleBulb();
mockBulb.AssertToggleWasCalled();
}
}
public class Switch {
private Bulb myBulb;
public Switch() {
myBulb = new Bulb();
}
public Switch(Bulb useThisBulbInstead) {
myBulb = useThisBulbInstead;
}
public void ToggleBulb() {
...
myBulb.Toggle();
...
}
}`
Co to jest wstrzykiwanie zależności (DI)?
Jak powiedzieli inni, Dependency Injection (DI) usuwa odpowiedzialność za bezpośrednie tworzenie i zarządzanie długością życia, innych obiektów obiektowych, od których zależy nasza klasa zainteresowań (klasa konsumentów) (w sensie UML ). Te instancje są zamiast tego przekazywane do naszej klasy konsumenta, zwykle jako parametry konstruktora lub za pomocą ustawiaczy właściwości (zarządzanie instancją obiektu zależności i przekazywaniem do klasy konsumenta jest zwykle wykonywane przez kontener Inversion of Control (IoC) , ale to inny temat) .
DI, DIP i SOLID
W szczególności, w paradygmacie Roberta C Martina SOLID zasad Object Oriented projekt , DIjest jednym z możliwych implementacji Dependency Inversion Principle (DIP) . DIP jest Dz SOLIDmantrą - inne implementacje DIP obejmują lokalizatora usług i wzorców wtyczki.
Celem DIP jest oddzielenie szczelne, betonowe zależności między klasami, a zamiast tego, aby poluzować sprzęgło za pomocą abstrakcji, co można osiągnąć poprzez interface, abstract classlub pure virtual class, w zależności od języka i podejścia stosowanego.
Bez DIP nasz kod (nazwałam tę „klasą konsumpcyjną”) jest bezpośrednio połączony z konkretną zależnością, a także często jest obciążony odpowiedzialnością za uzyskanie i zarządzanie instancją tej zależności, tj. Koncepcyjnie:
"I need to create/use a Foo and invoke method `GetBar()`"
Podczas gdy po zastosowaniu DIP wymóg został rozluźniony, a problem uzyskiwania i zarządzania Fooczasem trwania zależności został usunięty:
"I need to invoke something which offers `GetBar()`"
Dlaczego warto korzystać z DIP (i DI)?
Oddzielenie zależności między klasami w ten sposób pozwala na łatwe zastąpienie tych klas zależności innymi implementacjami, które również spełniają warunki abstrakcji (np. Zależność można przełączać za pomocą innej implementacji tego samego interfejsu). Ponadto, jak wspominają inni, ewentualnie Najczęstszym powodem do klas oddzielić poprzez DIP jest umożliwienie spożywania klasy należy badać w izolacji, ponieważ te same zależności mogą być teraz zgaszone i / lub wyśmiewany.
Jedną z konsekwencji DI jest to, że zarządzanie żywotnością instancji obiektów zależności nie jest już kontrolowane przez konsumującą klasę, ponieważ obiekt zależności jest teraz przekazywany do konsumującej klasy (poprzez wstrzyknięcie konstruktora lub ustawiacza).
Można to zobaczyć na różne sposoby:
Createw fabryce w razie potrzeby i zlikwidować je po zakończeniu.Kiedy stosować DI?
MyDepClassczy wątek jest bezpieczny - co, jeśli zrobimy z niego singleton i wstrzykniemy ten sam przypadek wszystkim konsumentom?)Przykład
Oto prosta implementacja w języku C #. Biorąc pod uwagę poniższą klasę konsumpcyjną:
public class MyLogger
{
public void LogRecord(string somethingToLog)
{
Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
}
}
Choć z pozoru nieszkodliwy, ma dwie staticzależności od dwóch innych klas System.DateTimei System.Console, co nie tylko ogranicza opcje wyjściowe rejestrowania (logowanie do konsoli będzie bezwartościowe, jeśli nikt nie patrzy), ale co gorsza, trudno jest automatycznie przetestować, biorąc pod uwagę zależność od niedeterministyczny zegar systemowy.
Możemy jednak zastosować się DIPdo tej klasy, wyodrębniając obawę związaną z oznaczaniem czasu jako zależnością i łącząc MyLoggertylko z prostym interfejsem:
public interface IClock
{
DateTime Now { get; }
}
Możemy również rozluźnić zależność od Consoleabstrakcji, takiej jak a TextWriter. Wstrzykiwanie zależności jest zwykle realizowane albo jako constructorwstrzyknięcie (przekazanie abstrakcji do zależności jako parametru do konstruktora klasy konsumującej) lub Setter Injection(przekazanie zależności przez setXyz()ustawiacz lub właściwość .Net ze {set;}zdefiniowaną). Preferowany jest Constructor Injection, ponieważ gwarantuje to, że klasa będzie w poprawnym stanie po zbudowaniu i pozwoli na oznaczenie wewnętrznych pól zależności jako readonly(C #) lub final(Java). Tak więc używając zastrzyku konstruktora w powyższym przykładzie, pozostawia nam to:
public class MyLogger : ILogger // Others will depend on our logger.
{
private readonly TextWriter _output;
private readonly IClock _clock;
// Dependencies are injected through the constructor
public MyLogger(TextWriter stream, IClock clock)
{
_output = stream;
_clock = clock;
}
public void LogRecord(string somethingToLog)
{
// We can now use our dependencies through the abstraction
// and without knowledge of the lifespans of the dependencies
_output.Write("{0:yyyy-MM-dd HH:mm:ss} - {1}", _clock.Now, somethingToLog);
}
}
(Należy Clockpodać konkret, do którego można oczywiście wrócić DateTime.Now, a dwie zależności muszą być zapewnione przez kontener IoC poprzez wstrzyknięcie konstruktora)
Można zbudować automatyczny test jednostkowy, który ostatecznie dowodzi, że nasz rejestrator działa poprawnie, ponieważ teraz mamy kontrolę nad zależnościami - czasem i możemy szpiegować zapisywane dane wyjściowe:
[Test]
public void LoggingMustRecordAllInformationAndStampTheTime()
{
// Arrange
var mockClock = new Mock<IClock>();
mockClock.Setup(c => c.Now).Returns(new DateTime(2015, 4, 11, 12, 31, 45));
var fakeConsole = new StringWriter();
// Act
new MyLogger(fakeConsole, mockClock.Object)
.LogRecord("Foo");
// Assert
Assert.AreEqual("2015-04-11 12:31:45 - Foo", fakeConsole.ToString());
}
Następne kroki
Wstrzykiwanie zależności jest niezmiennie związane z kontenerem Inwersji Kontroli (IoC) , aby wstrzykiwać (udostępniać) konkretne wystąpienia zależności i zarządzać instancjami długości życia. Podczas procesu konfiguracji / ładowania początkowego IoCkontenery umożliwiają zdefiniowanie następujących elementów:
IBar, zwraca ConcreteBarinstancję” )IDisposablei przyjmują odpowiedzialność za Disposingzależności zgodnie ze skonfigurowanym zarządzaniem żywotnością.Zazwyczaj po skonfigurowaniu / załadowaniu kontenerów IoC działają one płynnie w tle, umożliwiając koderowi skupienie się na dostępnym kodzie zamiast martwienia się o zależności.
Kluczem do kodu przyjaznego DI jest unikanie statycznego łączenia klas, a nie używanie new () do tworzenia zależności
Jak w powyższym przykładzie, odsprzężenie zależności wymaga pewnego wysiłku projektowego, a dla dewelopera konieczna jest zmiana paradygmatu, aby przełamać nawyk newbezpośredniego uzgadniania zależności i zamiast tego ufać kontenerowi w zarządzaniu zależnościami.
Ale korzyści jest wiele, zwłaszcza możliwość dokładnego przetestowania klasy zainteresowań.
Uwaga : Tworzenie / mapowanie / projekcja (via new ..()) POCO / POJO / Serialization DTOs / Entity Graphs / Anonymous JSON prognoz i in. - tj. Klasy lub rekordy „Tylko dane” - używane lub zwracane z metod nie są uważane za Zależności (w UML sense) i nie podlega DI. Używanie newdo wyświetlania jest w porządku.
Cały sens Dependency Injection (DI) polega na utrzymaniu kodu źródłowego aplikacji w czystości i stabilności :
Praktycznie każdy wzorzec projektu oddziela obawy, aby przyszłe zmiany wpływały na minimalne pliki.
Specyficzną domeną DI jest delegowanie konfiguracji zależności i inicjalizacja.
Jeśli od czasu do czasu pracujesz poza Javą, przypomnij sobie, jak sourceczęsto jest używana w wielu językach skryptowych (Shell, Tcl itp., A nawet importw Pythonie niewłaściwie wykorzystywanych do tego celu).
Rozważ prosty dependent.shskrypt:
#!/bin/sh
# Dependent
touch "one.txt" "two.txt"
archive_files "one.txt" "two.txt"
Skrypt jest zależny: nie zadziała samodzielnie ( archive_filesnie jest zdefiniowany).
Zdefiniować archive_filesw archive_files_zip.shskrypcie realizacji (używając zipw tym przypadku):
#!/bin/sh
# Dependency
function archive_files {
zip files.zip "$@"
}
Zamiast source-ing skryptu implementacyjnego bezpośrednio w skrypcie zależnym, używasz injector.sh„kontenera”, który otacza oba „komponenty”:
#!/bin/sh
# Injector
source ./archive_files_zip.sh
source ./dependent.sh
archive_files Zależność właśnie został wstrzyknięty do zależnego skryptu.
Mogłeś wstrzyknąć zależność, która implementuje archive_filesza pomocą tarlub xz.
Gdyby dependent.shskrypt używał zależności bezpośrednio, podejście nazwano by wyszukiwaniem zależności (co jest odwrotne do wstrzykiwania zależności ):
#!/bin/sh
# Dependent
# dependency look-up
source ./archive_files_zip.sh
touch "one.txt" "two.txt"
archive_files "one.txt" "two.txt"
Problem polega na tym, że zależny „komponent” musi sam dokonać inicjalizacji.
Kod źródłowy „komponentu” nie jest ani czysty, ani stabilny, ponieważ każda zmiana w inicjalizacji zależności wymaga również nowej wersji pliku kodu źródłowego „komponentów”.
DI nie jest tak mocno podkreślany i popularyzowany jak w frameworkach Java.
Ale jest to ogólne podejście do podzielenia obaw:
Używanie konfiguracji tylko z wyszukiwaniem zależności nie pomaga, ponieważ liczba parametrów konfiguracyjnych może się zmieniać w zależności od zależności (np. Nowy typ uwierzytelnienia), a także liczba obsługiwanych typów zależności (np. Nowy typ bazy danych).
Wszystkie powyższe odpowiedzi są dobre, moim celem jest wyjaśnienie pojęcia w prosty sposób, aby każdy bez znajomości programowania mógł zrozumieć pojęcie
Wstrzykiwanie zależności jest jednym z wzorców projektowych, które pomagają nam tworzyć złożone systemy w prostszy sposób.
Widzimy szeroką gamę zastosowań tego wzoru w naszym codziennym życiu. Niektóre z przykładów to magnetofon, VCD, napęd CD itp.
Powyższe zdjęcie jest obrazem przenośnego magnetofonu szpulowego z połowy XX wieku. Źródło .
Podstawowym celem magnetofonu jest nagrywanie lub odtwarzanie dźwięku.
Podczas projektowania systemu wymaga szpuli do nagrywania lub odtwarzania dźwięku lub muzyki. Istnieją dwie możliwości zaprojektowania tego systemu
Jeśli użyjemy pierwszego, musimy otworzyć maszynę, aby zmienić rolkę. jeśli zdecydujemy się na drugi, czyli zakładanie haka na rolkę, uzyskujemy dodatkową korzyść z grania dowolnej muzyki poprzez zmianę rolki. a także redukując funkcję tylko do grania na rolce.
Podobnie jak mądry wstrzykiwanie zależności jest procesem uzewnętrzniania zależności, aby skupić się tylko na konkretnej funkcjonalności komponentu, tak aby niezależne komponenty mogły być ze sobą łączone w celu utworzenia złożonego systemu.
Główne korzyści, które osiągnęliśmy dzięki zastosowaniu wstrzykiwania zależności.
Teraz dni ta koncepcja stanowi podstawę dobrze znanych ram w świecie programowania. Spring Angular itp. To dobrze znane frameworki programowe zbudowane na szczycie tej koncepcji
Wstrzykiwanie zależności to wzorzec używany do tworzenia instancji obiektów, na których polegają inne obiekty bez wiedzy w czasie kompilacji, która klasa zostanie użyta do zapewnienia tej funkcjonalności lub po prostu sposób wstrzykiwania właściwości do obiektu nazywa się wstrzykiwaniem zależności.
Przykład wstrzyknięcia zależności
Wcześniej piszemy taki kod
Public MyClass{
DependentClass dependentObject
/*
At somewhere in our code we need to instantiate
the object with new operator inorder to use it or perform some method.
*/
dependentObject= new DependentClass();
dependentObject.someMethod();
}
W przypadku wstrzykiwania zależności wtryskiwacz zależności usunie dla nas instancję
Public MyClass{
/* Dependency injector will instantiate object*/
DependentClass dependentObject
/*
At somewhere in our code we perform some method.
The process of instantiation will be handled by the dependency injector
*/
dependentObject.someMethod();
}
Możesz także przeczytać
Wstrzykiwanie zależności (DI) oznacza rozdzielanie obiektów, które są od siebie zależne. Powiedzmy, że obiekt A jest zależny od obiektu B, więc pomysł polega na oddzieleniu tych obiektów od siebie. Nie musimy na stałe kodować obiektu przy użyciu nowego słowa kluczowego, zamiast współdzielić zależności między obiektami w czasie wykonywania, pomimo czasu kompilacji. Jeśli o tym porozmawiamy
Nie musimy na stałe kodować obiektu za pomocą nowego słowa kluczowego, a raczej zdefiniować zależność fasoli w pliku konfiguracyjnym. Kontener sprężynowy będzie odpowiedzialny za podłączenie wszystkich.
MKOl jest ogólną koncepcją i można ją wyrazić na wiele różnych sposobów, a wstrzykiwanie zależności jest jednym konkretnym przykładem MKOl.
DI oparte na konstruktorze jest osiągane, gdy kontener wywołuje konstruktor klasy z wieloma argumentami, z których każdy reprezentuje zależność od innej klasy.
public class Triangle {
private String type;
public String getType(){
return type;
}
public Triangle(String type){ //constructor injection
this.type=type;
}
}
<bean id=triangle" class ="com.test.dependencyInjection.Triangle">
<constructor-arg value="20"/>
</bean>
DI oparte na seterach jest osiągane przez kontener wywołujący metody setter dla twoich ziaren po wywołaniu konstruktora bez argumentu lub statycznej metody bez argumentu w celu utworzenia instancji komponentu bean.
public class Triangle{
private String type;
public String getType(){
return type;
}
public void setType(String type){ //setter injection
this.type = type;
}
}
<!-- setter injection -->
<bean id="triangle" class="com.test.dependencyInjection.Triangle">
<property name="type" value="equivialteral"/>
UWAGA: Dobrą zasadą jest stosowanie argumentów konstruktora dla obowiązkowych zależności, a ustawiaczy dla zależności opcjonalnych. Zauważ, że jeśli używamy adnotacji opartych na adnotacji @Required na seterach, można użyć ich do stworzenia seterów jako wymaganych zależności.
Najlepszą analogią, jaką mogę wymyślić, jest chirurg i jego asystent (asystenci) w sali operacyjnej, gdzie chirurg jest główną osobą i jego asystentem, który zapewnia różne elementy chirurgiczne, gdy tego potrzebuje, aby chirurg mógł skoncentrować się na jednym rzecz, którą robi najlepiej (operacja). Bez asystenta chirurg musi sam zdobyć komponenty za każdym razem, gdy tego potrzebuje.
Krótko mówiąc, DI jest techniką polegającą na usunięciu wspólnej dodatkowej odpowiedzialności (obciążenia) na komponentach za pobranie komponentów zależnych poprzez ich przekazanie.
DI przybliża cię do zasady pojedynczej odpowiedzialności (SR), takiej jak surgeon who can concentrate on surgery.
Kiedy stosować DI: Polecam używanie DI w prawie wszystkich projektach produkcyjnych (małych / dużych), szczególnie w ciągle zmieniających się środowiskach biznesowych :)
Powód: Ponieważ chcesz, aby Twój kod był łatwy do przetestowania, naśladowania itp., Abyś mógł szybko przetestować zmiany i wypchnąć go na rynek. Poza tym dlaczego nie miałbyś, skoro masz wiele niesamowitych darmowych narzędzi / ram, które wspierają cię w podróży do bazy kodu, w której masz większą kontrolę.
Przykład, mamy 2 klasy Clienti Service. Clientbędzie użytyService
public class Service {
public void doSomeThingInService() {
// ...
}
}
Sposób 1)
public class Client {
public void doSomeThingInClient() {
Service service = new Service();
service.doSomeThingInService();
}
}
Sposób 2)
public class Client {
Service service = new Service();
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
Sposób 3)
public class Client {
Service service;
public Client() {
service = new Service();
}
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
1) 2) 3) Korzystanie
Client client = new Client();
client.doSomeThingInService();
Zalety
Niedogodności
Clientklasy testowejServicekonstruktor, musimy zmienić kod we wszystkich Serviceobiektach tworzenia obiektuSposób 1) Wstrzyknięcie konstruktora
public class Client {
Service service;
Client(Service service) {
this.service = service;
}
// Example Client has 2 dependency
// Client(Service service, IDatabas database) {
// this.service = service;
// this.database = database;
// }
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
Za pomocą
Client client = new Client(new Service());
// Client client = new Client(new Service(), new SqliteDatabase());
client.doSomeThingInClient();
Sposób 2) Wstrzyknięcie setera
public class Client {
Service service;
public void setService(Service service) {
this.service = service;
}
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
Za pomocą
Client client = new Client();
client.setService(new Service());
client.doSomeThingInClient();
Sposób 3) Wstrzyknięcie interfejsu
Czek https://en.wikipedia.org/wiki/Dependency_injection
===
Teraz ten kod jest już śledzony Dependency Injectioni jest łatwiej dla Clientklasy testowej .
Jednak nadal używamy new Service()wiele czasu i nie jest dobrze, gdy Servicekonstruktor zmiany jest zmienny. Aby temu zapobiec, możemy użyć wtryskiwacza DI, takiego jak
1) Prosta instrukcjaInjector
public class Injector {
public static Service provideService(){
return new Service();
}
public static IDatabase provideDatatBase(){
return new SqliteDatabase();
}
public static ObjectA provideObjectA(){
return new ObjectA(provideService(...));
}
}
Za pomocą
Service service = Injector.provideService();
2) Użyj biblioteki: dla systemu Android sztylet2
Zalety
Service należy go zmienić tylko w klasie InjectorConstructor Injection, kiedy spojrzysz na konstruktor Client, zobaczysz ile zależności ClientklasNiedogodności
Constructor Injection, Serviceobiekt jest tworzony podczas Clienttworzenia, czasami używamy funkcji w Clientklasie bez użycia, Servicewięc utworzony Servicezostaje zmarnowanyhttps://en.wikipedia.org/wiki/Dependency_injection
Zależność to obiekt, którego można użyć (
Service)
Wstrzyknięcie to przekazanie zależności (Service) do obiektu zależnego (Client), który by go użył
Oznacza to, że obiekty powinny mieć tyle zależności, ile jest potrzebne do wykonania ich zadania, a zależności powinny być nieliczne. Co więcej, zależności obiektu powinny dotyczyć interfejsów, a nie „konkretnych” obiektów, o ile to możliwe. (Konkretny obiekt to każdy obiekt utworzony za pomocą słowa kluczowego new.) Luźne sprzężenie zapewnia większą możliwość ponownego użycia, łatwiejszą konserwację i umożliwia łatwe dostarczanie „próbnych” obiektów zamiast kosztownych usług.
„Wstrzykiwanie zależności” (DI) jest również znane jako „Inwersja kontroli” (IoC), może być stosowane jako technika zachęcania do tego luźnego sprzężenia.
Istnieją dwa podstawowe podejścia do wdrażania DI:
Jest to technika przekazywania zależności między obiektami do konstruktora.
Zauważ, że konstruktor akceptuje interfejs, a nie konkretny obiekt. Należy również pamiętać, że wyjątek jest zgłaszany, jeśli parametr orderlayer ma wartość NULL. Podkreśla to znaczenie otrzymania prawidłowej zależności. Constructor Injection jest moim zdaniem preferowanym mechanizmem nadawania obiektowi swoich zależności. Podczas wywoływania obiektu jest jasne dla dewelopera, jakie zależności należy podać obiektowi „Osoba” w celu poprawnego wykonania.
Ale rozważ następujący przykład… Załóżmy, że masz klasę z dziesięcioma metodami, które nie mają zależności, ale dodajesz nową metodę, która ma zależność od IDAO. Możesz zmienić konstruktora, aby używał Constructor Injection, ale może to zmusić cię do zmiany wszystkich wywołań konstruktora w dowolnym miejscu. Alternatywnie, możesz po prostu dodać nowego konstruktora, który bierze zależność, ale wtedy skąd programista łatwo wie, kiedy użyć jednego konstruktora nad drugim. Wreszcie, jeśli tworzenie zależności jest bardzo kosztowne, dlaczego należy ją utworzyć i przekazać konstruktorowi, skoro może być używana rzadko? „Zastrzykiwanie setera” to kolejna technika DI, którą można zastosować w takich sytuacjach.
Zastrzyk Settera nie zmusza do przekazania zależności do konstruktora. Zamiast tego zależności są ustawiane na właściwości publiczne ujawniane przez obiekt w potrzebie. Jak sugerowano poprzednio, głównymi czynnikami motywującymi do tego są:
Oto przykład, jak wyglądałby powyższy kod:
public class Person {
public Person() {}
public IDAO Address {
set { addressdao = value; }
get {
if (addressdao == null)
throw new MemberAccessException("addressdao" +
" has not been initialized");
return addressdao;
}
}
public Address GetAddress() {
// ... code that uses the addressdao object
// to fetch address details from the datasource ...
}
// Should not be called directly;
// use the public property instead
private IDAO addressdao;
Myślę, że skoro wszyscy pisali dla DI, zadam kilka pytań ...
Jest to oparte na opublikowanej odpowiedzi @Adam N.
Dlaczego PersonService nie musi się już martwić o GroupMembershipService? Wspomniałeś, że GroupMembership ma wiele rzeczy (obiektów / właściwości), od których zależy. Jeśli GMService był wymagany w PService, miałbyś go jako właściwość. Możesz wyśmiewać to, niezależnie od tego, czy wstrzyknąłeś go, czy nie. Jedynym momentem, w którym chciałbym, aby został wstrzyknięty, jest to, że GMService ma bardziej szczegółowe klasy potomne, których nie znasz do czasu uruchomienia. Następnie chcesz wstrzyknąć podklasę. Lub jeśli chcesz użyć tego jako singletonu lub prototypu. Szczerze mówiąc, plik konfiguracyjny ma wszystko zakodowane na stałe w zakresie podklasy dla typu (interfejsu), który zostanie wprowadzony podczas kompilacji.
EDYTOWAĆ
Miły komentarz Jose Maria Arranz na temat DI
DI zwiększa spójność poprzez usunięcie jakiejkolwiek potrzeby określenia kierunku zależności i napisania dowolnego kodu kleju.
Fałszywe. Kierunek zależności jest w formie XML lub jako adnotacje, a twoje zależności są zapisywane jako kod XML i adnotacje. XML i adnotacje SĄ kodem źródłowym.
DI zmniejsza sprzężenie, czyniąc wszystkie elementy modułowymi (tj. Wymiennymi) i ma dobrze zdefiniowane interfejsy.
Fałszywe. Nie potrzebujesz frameworka DI do zbudowania kodu modułowego opartego na interfejsach.
O wymiennych: dzięki bardzo prostemu archiwum .properties i Class.forName możesz zdefiniować, które klasy mogą być zmieniane. Jeśli JAKĄKOLWIEK klasę kodu można zmienić, Java nie jest dla Ciebie, użyj języka skryptowego. Nawiasem mówiąc: adnotacji nie można zmienić bez ponownej kompilacji.
Moim zdaniem istnieje jeden jedyny powód dla ram DI: redukcja płyty kotła. Dzięki dobrze wykonanemu systemowi fabrycznemu możesz zrobić to samo, bardziej kontrolowane i bardziej przewidywalne, jak preferowany framework DI, frameworki DI obiecują redukcję kodu (XML i adnotacje również są kodem źródłowym). Problem polega na tym, że redukcja płyty kotła jest po prostu realna w bardzo bardzo prostych przypadkach (jedna instancja na klasę i podobne), czasami w świecie rzeczywistym wybranie odpowiedniego obiektu usługi nie jest tak łatwe, jak odwzorowanie klasy na obiekt singletonu.
Popularne odpowiedzi są nieprzydatne, ponieważ definiują wstrzykiwanie zależności w sposób, który nie jest użyteczny. Zgódźmy się, że przez „zależność” rozumiemy jakiś wcześniejszy inny obiekt, którego potrzebuje nasz obiekt X. Ale nie mówimy, że robimy „zastrzyk zależności”, kiedy mówimy
$foo = Foo->new($bar);
Po prostu nazywamy to przekazywaniem parametrów do konstruktora. Robimy to regularnie od momentu wynalezienia konstruktorów.
„Wstrzykiwanie zależności” jest uważane za rodzaj „odwrócenia kontroli”, co oznacza, że część logiki jest usuwana z dzwoniącego. Nie dzieje się tak, gdy wywołujący przekazuje parametry, więc jeśli byłyby to DI, DI nie oznaczałoby odwrócenia kontroli.
DI oznacza, że istnieje poziom pośredni między dzwoniącym a konstruktorem, który zarządza zależnościami. Makefile to prosty przykład wstrzykiwania zależności. „Osoba dzwoniąca” to osoba wpisująca „make bar” w wierszu poleceń, a „konstruktor” to kompilator. Plik Makefile określa, że pasek zależy od foo i robi to
gcc -c foo.cpp; gcc -c bar.cpp
przed zrobieniem
gcc foo.o bar.o -o bar
Osoba wpisująca „make bar” nie musi wiedzieć, że pasek zależy od foo. Wstrzyknięto zależność między „make bar” a gcc.
Głównym celem poziomu pośredniego jest nie tylko przekazywanie zależności do konstruktora, ale także lista wszystkich zależności w jednym miejscu i ukrycie ich przed koderem (nie zmuszanie kodera do ich dostarczenia).
Zwykle poziom pośredni zapewnia fabryki budowanych obiektów, które muszą spełniać rolę, którą musi spełniać każdy żądany typ obiektu. Jest tak, ponieważ mając poziom pośredni, który ukrywa szczegóły budowy, poniosłeś już karę abstrakcji nałożoną przez fabryki, więc równie dobrze możesz korzystać z fabryk.
Wstrzykiwanie zależności oznacza sposób (właściwie dowolny sposób ), aby jedna część kodu (np. Klasa) miała dostęp do zależności (innych części kodu, np. Innych klas, od których to zależy) w sposób modułowy, bez ich zakodowania (więc można je dowolnie zmieniać lub zastępować, a nawet w razie potrzeby ładować w innym czasie)
(i ps, tak, to stało się zbyt hiper-25-calową nazwą dla raczej prostej koncepcji) , moje .25centy
Wiem, że jest już wiele odpowiedzi, ale znalazłem to bardzo pomocne: http://tutorials.jenkov.com/dependency-injection/index.html
public class MyDao {
protected DataSource dataSource = new DataSourceImpl(
"driver", "url", "user", "password");
//data access methods...
public Person readPerson(int primaryKey) {...}
}
public class MyDao {
protected DataSource dataSource = null;
public MyDao(String driver, String url, String user, String password) {
this.dataSource = new DataSourceImpl(driver, url, user, password);
}
//data access methods...
public Person readPerson(int primaryKey) {...}
}
Zauważ, jak DataSourceImplinstancja jest przenoszona do konstruktora. Konstruktor przyjmuje cztery parametry, które są czterema wartościami potrzebnymi przez DataSourceImpl. Chociaż MyDaoklasa nadal zależy od tych czterech wartości, sama nie spełnia już tych zależności. Są dostarczane przez dowolną klasę tworzącą MyDaoinstancję.
Wstrzykiwanie zależności jest jednym z możliwych rozwiązań, które można ogólnie nazwać wymogiem „zaciemniania zależności”. Obfuskacja zależności jest metodą usuwania „oczywistej” natury z procesu zapewniania zależności klasie, która tego wymaga, a zatem zaciemniania, w pewien sposób, udostępniania wspomnianej zależności tej klasie. To niekoniecznie jest zła rzecz. W rzeczywistości, zaciemniając sposób, w jaki zależność jest przekazywana do klasy, wówczas coś poza klasą jest odpowiedzialne za tworzenie zależności, co oznacza, że w różnych scenariuszach inna implementacja zależności może być dostarczona do klasy bez wprowadzania żadnych zmian do klasy. Jest to doskonałe do przełączania między trybem produkcyjnym a testowym (np. Przy użyciu zależności „próbnej” usługi).
Niestety wadą jest to, że niektórzy ludzie założyli, że potrzebujesz specjalistycznego frameworka do zaciemniania zależności i że jesteś w jakiś sposób „mniejszym” programistą, jeśli zdecydujesz się nie używać do tego konkretnego frameworka. Kolejnym, niezwykle niepokojącym mitem, uważanym przez wielu, jest to, że zastrzyk uzależnienia jest jedynym sposobem na osiągnięcie zaciemnienia zależności. Jest to wyraźnie i historycznie i oczywiście w 100% błędne, ale będziesz miał trudności z przekonaniem niektórych osób, że istnieją alternatywy dla zastrzyku uzależnienia dla twoich wymagań zaciemniania zależności.
Programiści rozumieli wymóg zaciemniania zależności od lat i wiele alternatywnych rozwiązań ewoluowało zarówno przed, jak i po wymyśleniu zastrzyku zależności. Istnieją wzorce fabryczne, ale istnieje także wiele opcji wykorzystujących ThreadLocal, w których nie jest wymagane wstrzykiwanie do konkretnej instancji - zależność jest skutecznie wstrzykiwana do wątku, co ma tę zaletę, że udostępnia obiekt (za pomocą wygodnych metod statycznego pobierania) do dowolnegoklasa, która tego wymaga, bez konieczności dodawania adnotacji do klas, które tego wymagają, i skonfigurowania skomplikowanego „kleju” XML, aby tak się stało. Kiedy twoje zależności są wymagane do trwałości (JPA / JDO lub cokolwiek innego), pozwala to osiągnąć „tranaparent uporczywość” znacznie łatwiej i dzięki modelom domen i klasom modeli biznesowych złożonym wyłącznie z POJO (tj. Bez specyficznych ram / zablokowanych w adnotacjach).
Z książki „ Well-Grounded Java Developer: Istotne techniki programowania Java 7 i polyglot
DI jest szczególną formą IoC, w której proces znajdowania zależności jest poza bezpośrednią kontrolą aktualnie wykonywanego kodu.
Zanim przejdę do opisu technicznego, najpierw zwizualizuj go na przykładzie z życia, ponieważ znajdziesz wiele technicznych rzeczy do nauczenia się wstrzykiwania zależności, ale maksymalny czas, jaki ludzie tacy jak ja, nie mogą zrozumieć podstawowej koncepcji tego.
Na pierwszym zdjęciu Załóżmy, że masz fabrykę samochodów z wieloma jednostkami. Samochód jest faktycznie wbudowany w zespół montażowy, ale potrzebuje silnika , siedzeń oraz kół . Więc jednostki montażowej zależy od tych wszystkich jednostek i są Zależności fabryki.
Możesz czuć, że utrzymanie wszystkich zadań w tej fabryce jest zbyt skomplikowane, ponieważ wraz z głównym zadaniem (Montaż samochodu w zespole montażowym) musisz skupić się również na innych jednostkach . Utrzymanie jest teraz bardzo kosztowne, a budynek fabryki jest ogromny, więc możesz wziąć dodatkowe dolary do wynajęcia.
Teraz spójrz na drugie zdjęcie. Jeśli znajdziesz firmy oferujące tanie koła , siedzenia i silnik, które są tańsze niż koszty własnej produkcji, nie musisz robić ich w swojej fabryce. Możesz teraz wynająć mniejszy budynek tylko dla swojego zespołu montażowego, co zmniejszy zadanie konserwacji i zmniejszy dodatkowe koszty wynajmu. Teraz możesz skupić się tylko na głównym zadaniu (montaż samochodu).
Teraz możemy powiedzieć, że wszystkie zależności do montażu samochodu są wstrzykiwane fabrycznie od dostawców . Jest to przykład rzeczywistego wtrysku zależności (DI) .
Innymi słowy, wstrzykiwanie zależności jest techniką, w której jeden obiekt (lub metoda statyczna) dostarcza zależności drugiego obiektu. Tak więc przeniesienie zadania tworzenia obiektu na inną osobę i bezpośrednie użycie zależności nazywa się wstrzykiwaniem zależności.
To pomoże ci teraz nauczyć DI z jakimś słowem techy. To pokaże, kiedy używać DI, a kiedy nie .
z książki Apress.Spring.Persistence.w.Hibernate.Oct.2010
Wstrzykiwanie zależności ma na celu oddzielenie pracy polegającej na usuwaniu zewnętrznych składników oprogramowania z logiki biznesowej aplikacji. Bez wstrzykiwania zależności szczegółowe informacje o tym, jak składnik uzyskuje dostęp do wymaganych usług, mogą zostać zakłócone przez kod tego składnika. Zwiększa to nie tylko ryzyko błędów, dodaje rozdęcie kodu i zwiększa złożoność konserwacji; łączy ściślej ze sobą komponenty, co utrudnia modyfikację zależności podczas refaktoryzacji lub testowania.
Dependency Injection (DI) to jeden z Design Patterns, który wykorzystuje podstawową cechę OOP - relację jednego obiektu z innym obiektem. Podczas gdy dziedziczenie dziedziczy jeden obiekt, aby wykonać bardziej złożony i specyficzny inny obiekt, relacja lub powiązanie po prostu tworzy wskaźnik do innego obiektu z jednego obiektu za pomocą atrybutu. Moc DI jest połączona z innymi funkcjami OOP, podobnie jak interfejsy i ukrywanie kodu. Załóżmy, że mamy w bibliotece klienta (subskrybenta), który dla uproszczenia może wypożyczyć tylko jedną książkę.
Interfejs książki:
package com.deepam.hidden;
public interface BookInterface {
public BookInterface setHeight(int height);
public BookInterface setPages(int pages);
public int getHeight();
public int getPages();
public String toString();
}
Następnie możemy mieć wiele rodzajów książek; jednym z nich jest fikcja:
package com.deepam.hidden;
public class FictionBook implements BookInterface {
int height = 0; // height in cm
int pages = 0; // number of pages
/** constructor */
public FictionBook() {
// TODO Auto-generated constructor stub
}
@Override
public FictionBook setHeight(int height) {
this.height = height;
return this;
}
@Override
public FictionBook setPages(int pages) {
this.pages = pages;
return this;
}
@Override
public int getHeight() {
// TODO Auto-generated method stub
return height;
}
@Override
public int getPages() {
// TODO Auto-generated method stub
return pages;
}
@Override
public String toString(){
return ("height: " + height + ", " + "pages: " + pages);
}
}
Teraz subskrybent może mieć powiązanie z książką:
package com.deepam.hidden;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Subscriber {
BookInterface book;
/** constructor*/
public Subscriber() {
// TODO Auto-generated constructor stub
}
// injection I
public void setBook(BookInterface book) {
this.book = book;
}
// injection II
public BookInterface setBook(String bookName) {
try {
Class<?> cl = Class.forName(bookName);
Constructor<?> constructor = cl.getConstructor(); // use it for parameters in constructor
BookInterface book = (BookInterface) constructor.newInstance();
//book = (BookInterface) Class.forName(bookName).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return book;
}
public BookInterface getBook() {
return book;
}
public static void main(String[] args) {
}
}
Wszystkie trzy klasy można ukryć dla własnej implementacji. Teraz możemy użyć tego kodu dla DI:
package com.deepam.implement;
import com.deepam.hidden.Subscriber;
import com.deepam.hidden.FictionBook;
public class CallHiddenImplBook {
public CallHiddenImplBook() {
// TODO Auto-generated constructor stub
}
public void doIt() {
Subscriber ab = new Subscriber();
// injection I
FictionBook bookI = new FictionBook();
bookI.setHeight(30); // cm
bookI.setPages(250);
ab.setBook(bookI); // inject
System.out.println("injection I " + ab.getBook().toString());
// injection II
FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set
System.out.println("injection II " + ab.getBook().toString());
}
public static void main(String[] args) {
CallHiddenImplBook kh = new CallHiddenImplBook();
kh.doIt();
}
}
Istnieje wiele różnych sposobów użycia wstrzykiwania zależności. Możliwe jest połączenie go z Singletonem itp., Ale nadal w podstawowym przypadku jest to tylko skojarzenie realizowane przez utworzenie atrybutu typu obiektu w innym obiekcie. Przydatność polega tylko i wyłącznie na tym, że ten kod, który powinniśmy pisać od nowa, jest zawsze dla nas przygotowany i wykonywany. Właśnie dlatego DI jest tak ściśle powiązany z Inversion of Control (IoC), co oznacza, że nasz program przekazuje sterowanie innemu działającemu modułowi, który wykonuje wstrzykiwanie fasoli do naszego kodu. (Każdy obiekt, który można wstrzyknąć, może zostać podpisany lub uznany za Fasolę.) Na przykład wiosną robi się to poprzez utworzenie i inicjalizację ApplicationContextkontener, który działa dla nas. Po prostu w naszym kodzie tworzymy kontekst i wywołujemy inicjalizację ziaren. W tym momencie wstrzyknięcie zostało wykonane automatycznie.
Zastrzyk zależny dla 5-latków.
Kiedy idziesz i wyciągasz rzeczy z lodówki dla siebie, możesz powodować problemy. Możesz zostawić otwarte drzwi, możesz dostać coś, czego mama lub tata nie chcą, żebyś miał. Być może szukasz czegoś, czego nawet nie mamy lub które wygasło.
To, co powinieneś zrobić, to stwierdzenie potrzeby: „Potrzebuję czegoś do picia z lunchem”, a wtedy upewnimy się, że masz coś, kiedy usiądziesz i coś zjeść.
Od Christoffera Noringa, książki Pablo Deelemana „Learning Angular - Second Edition”:
„Gdy nasze aplikacje rosną i ewoluują, każda z naszych jednostek kodu będzie wewnętrznie wymagać wystąpień innych obiektów , które w świecie inżynierii oprogramowania są lepiej znane jako zależności . Działanie polegające na przekazywaniu takich zależności do zależnego klienta jest znane jako wstrzykiwanie , i pociąga za sobą także udział innej jednostki kodowej o nazwie iniektor . Inżektor będzie odpowiedzialny za tworzenie instancji i ładowanie systemu wymaganych zależnościdzięki czemu są gotowe do użycia od momentu pomyślnego wstrzyknięcia do klienta. Jest to bardzo ważne, ponieważ klient nie wie nic o tym, jak utworzyć własną zależność i jest świadomy tylko interfejsu, który implementuje w celu ich użycia ”.
Od: Anton Moiseev. książka „Angular Development with Typescript, wydanie drugie”.:
„Krótko mówiąc, DI pomaga ci pisać kod w luźny sposób i sprawia, że kod jest bardziej testowalny i można go ponownie wykorzystać .”
W prostych słowach wstrzykiwanie zależności (DI) to sposób na usunięcie zależności lub ścisłe połączenie między różnymi obiektami. Wstrzykiwanie zależności zapewnia spójne zachowanie każdego obiektu.
DI to implementacja zasady MKOl wiosny, która mówi „Nie dzwoń do nas, my zadzwonimy”. Za pomocą programisty wstrzykiwania zależności nie trzeba tworzyć obiektu przy użyciu nowego słowa kluczowego.
Obiekty są raz ładowane do kontenera Spring, a następnie ponownie ich używamy, gdy tylko ich potrzebujemy, pobierając te obiekty z kontenera Spring przy użyciu metody getBean (String beanName).
Zastrzyk zależności jest sercem koncepcji związanej z Spring Framework. Podczas tworzenia szkieletu dowolnego projektu sprężyna może odgrywać istotną rolę, a tutaj zastrzyk zależności pojawia się w dzbanie.
W rzeczywistości załóżmy, że w java stworzyłeś dwie różne klasy, takie jak klasa A i klasa B, i niezależnie od funkcji dostępnej w klasie B, której chcesz użyć w klasie A, więc w tym czasie można zastosować wstrzykiwanie zależności. gdzie możesz utworzyć skrzynkę obiektu jednej klasy w drugiej, w ten sam sposób możesz wstrzyknąć całą klasę w inną klasę, aby była dostępna. w ten sposób można przezwyciężyć zależność.
WTRYSK ZALEŻNOŚCI JEST PO PROSTU SKLEJANIE DWÓCH KLAS I W TYM SAMYM CZASIE UTRZYMYWANIE JEGO ODDZIELNIE.