Dependency injection
Dependency injection (DI) tõlgitakse eesti keelde kui sõltuvuste sisestamine. Selle all mõeldakse programmilist lähenemist kus klass ei loo ise süsteemi objekte, vaid need antakse talle nö. väljapoolt ette.
Vaatleme järgnevat näidet, mis joonistab DI paremini lahti.
// Klass loob ise loggeri objekti
public class SomeClass
{
private readonly ILogger _logger;
public SomeClass()
{
_logger = new FileLogger();
}
}
// Logger antakse kaasa klassi konstruktorisse
public class SomeClassWithDI
{
private readonly ILogger _logger;
public SomeClass(ILogger logger)
{
_logger = logger;
}
}
Esimesel juhul teab klass SomeClass
kuidas logger luua. Teisel juhul antakse logger klassile SomeClassWithDI
konstruktorisse kaasa. Klass SomeClassWithDI
kasutab loggerit, mis talle kaasa anti. Selliselt töötab DI klasside tasemel.
Milleks DI?
DI annab meie süsteemidele juurde paindlikkust. Kui me soovime, siis võime me panna klassi kasutama mõnda teist tüüpi objekti ilma, et me peaksime klassi enda koodi muutma. Järgnev näide illustreerib kuidas me võime samale klassile kaasa anda kord ühe ja kord teise loggeri.
public interface ILogger
{
void Log(string message);
}
public class ConsoleLogger : ILogger
{
void Log(string message)
{
Console.WriteLine(message);
}
}
public class DebugLogger : ILogger
{
void Log(string message)
{
Debug.WriteLine(message);
}
}
public class SomeClass
{
private readonly ILogger _logger;
public SomeClass(ILogger logger)
{
_logger = logger;
}
public void DoSomething()
{
_logger.Log("Hello, World!");
}
}
var someClass1 = new SomeClass(new ConsoleLogger());
var someClass2 = new SomeClass(new DebugLogger());
someClass1.DoSomething();
someClass2.DoSomething();
DI konteinerid
Jäänud on veel paar küsimust:
- Kuidas otsustada süsteemiüleselt milline klass millisele teisele klassile konstruktorisse kaasa anda?
- Kas saaks objektide loomist kuidagi automatiseerida selliselt, et me ütleme, mis tüüpi objekti me soovime ja me saame vastava objekti kätte ilma, et peaksime seda ise konstrueerima?
Vastus mõlemale küsimusele on õnneks jaatav. Selle jaoks on olemas spetsiaalsed komponendid, mida kutsutakse ka DI konteineriteks. Kui programm käima läheb, siis seadistatakse DI konteiner ära. DI konteinerile öeldakse ette klassid, millest ta peab oskama objekte luua ning edasi kasutame me süsteemis läbivalt vastavat konteinerit.
???
DI pole meile tegelikult võõras, sest oleme seda kasutanud antud aine algusest saadik. Meenutage kuidas me oma rakenduses andsime kontrolleritesse kaasa andmekonteksti konstruktori kaudu. Rohkemat midagi me tegema ei pidanud - andmekontekst lihtsalt saabus äkki kontrolleritesse. ASP.NET Core korral on DI juba veebiraamistikku sisse ehitatud ja selle kasutamine on ka algajatele hästi lihtne.
Kuidas DI töötab
Selleks, et eelnev pilt tükkidest kokku panna ja teemas paremat selgust saada, vaatame järgmist joonist.
- Client - klass, millesse sõltuvused sisestatakse (loe: klass, mille konstruktorisse antakse DI konteineri abil objekte kaasa)
- Injector - DI konteiner ehk komponent, mis oskab ise objekte luua ja neisse sõltuvused kaasa anda
- Service - klassid, mida antakse klientklassi konstruktorisse kaasa
- Interface - interface, mida tunneb nii klientklass kui ka DI konteiner