Pobierz prezentację
Pobieranie prezentacji. Proszę czekać
1
Adam Suwała DIY - DI
2
Agenda: Dependency Injection - trochę podstaw
Kontenery DI - masz problem, weź framework Service Locator - anti-pattern Do-It-Yourself Dependency Injection
3
Dependency Injection - trochę podstaw
4
Definicje z WIKI • Wstrzykiwanie zależności (Dependency Injection, DI) – wzorzec projektowy i wzorzec architektury oprogramowania polegający na usuwaniu bezpośrednich zależności pomiędzy komponentami na rzecz architektury typu plug-in. • Odwrócenie sterowania (Inversion of Control, IoC) – paradygmat polegający na przeniesieniu na zewnątrz komponentu (np. obiektu) odpowiedzialności za kontrolę wybranych czynności.
5
Po co DI? sprawić żeby kod miał miej powiązań lub powiązania były „luźniejsze” (loose coupling). ułatwić testy jednostkowe kodu (automatyczne testy pojedynczych klas w izolacji od innych)
6
Rodzaje wstrzykiwania zależności:
Constructor Injection Setter Injection Interface Injection
7
Jak to wygląda w praktyce?
Tradycyjne podejście: class Mail { //… public bool Wyslij() var klientSmtp = new KlientSmtp(); return klientSmtp.Wyslij(adres, tytul, tresc); }
8
Podejście DI: class Mail { private readonly IKlientSmtp klientSmtp; public Mail(IKlientSmtp klientSmtp) this.klientSmtp = klientSmtp; } //… public bool Wyslij() var klientSmtp = new KlientSmtp(); return klientSmtp.Wyslij(adres, tytul, tresc);
9
Skąd w DI biorą się zależności?
class Mail { private readonly IKlientSmtp klientSmtp; public Mail(IKlientSmtp klientSmtp) this.klientSmtp = klientSmtp; } //…
10
Kontenery - masz problem, weź framework
11
Frameworki DI dla .net, przykłady:
• Autofac • Castle.Windsor • Ninject • Sprint.net • StructureMap • Unity
12
Świadomy wybór kontenera DI
Różnice: • Składnia • Wsparcie dla różnych rozwiązań • Szybkość działania
13
Co fajnego potrafią robić kontenery DI?
14
Kaskada zależności – powiązania klas
class KlientTcp : IKlientTcp { /*…*/ } class KlientSmtp : IKlientSmtp { private readonly IKlientTcp klientTcp; public KlientSmtp(IKlientTcp klientTcp) { this.klientTcp = klientTcp; } /*…*/ } class Mail { private readonly IKlientSmtp klientSmtp; public Mail(IKlientSmtp klientSmtp) { this.klientSmtp = klientSmtp;
15
Kaskada zależności – jak złożyć?
class UzycieKontenera { void Przyklad() { Kontener.Register<IKlientTcp, KlientTcp>(); Kontener.Register<IKlientSmtp, KlientSmtp>(); //… var mail = Kontener.Resolve<Mail>(); }
16
Metody inicjowania kontenera
Bezpośrednie rejestrowanie w kodzie Pliki konfiguracyjne Z użyciem refleksji
17
Czas życia obiektów Transient Singleton Thread Singleton
Request Singleton
18
Dlaczego DI bez kontenera?
Nie jest konieczny do robienia DI Może zaciemniać kod Rzeczywiste zależności mogą nie być tak proste jak w przykładach Może prowokować do robienie DI źle
19
Service Locator - anti-pattern
20
Jak wygląda wzorzec Service Locator:
class Mail { //… public bool Wyslij() var klientSmtp = Kontener.Resolve<IKlientSmtp>(); return klientSmtp.Wyslij(adres, tytul, tresc); }
21
Do-It-Yourself Dependency Injection
22
„Chad Parry DIY-DI” – jak zrobić dobre DI bez kontenera.
23
DI powinniśmy używać konsekwentnie w całej aplikacji.
DI to sposób myślenia
24
Scope – techniczna klasa, która ma przechowywać parametry konfiguracyjne i uchwyty do obiektów
class ApplicationScope { private readonly string[] args; public ApplicationScope(string[] args) this.args = args; MaxX = 100; MaxY = 100; } public string[] Args { get { return args; } } public int MaxX { get; private set; } public int MaxY { get; private set; }
25
MainHelper – „biznesowo-pomocnicza” klasa, która umożliwia wstrzykiwanie zależności od samego startu programu class MainHelper { private readonly string[] args; private readonly IRobot robot; public MainHelper(string[] args, IRobot robot) this.args = args; this.robot = robot; } public void Run() // to co normalnie jest w main
26
Injector – techniczna klasa, której zadaniem jest składanie aplikacji i właściwe wstrzykiwanie zależności class ApplicationInjector { public MainHelper InjectMainHelper(ApplicationScope scope) return new MainHelper(scope.Args, InjectRobot(scope)); } private IRobot InjectRobot(ApplicationScope scope) return new Robot(scope.MaxX, scope.MaxY);
27
Start programu class Program { static void Main(string[] args) var scope = new ApplicationScope(args); new ApplicationInjector() .InjectMainHelper(scope) .Run(); }
28
Co zrobić ze skomplikowanymi przypadkami?
29
Fabryka – ale nie taka zwyczajna
public class FabrykaRobotow { private readonly Func<IRobot> robotBuild; private readonly Func<IRobot> robotLatajacyBuild; internal FabrykaRobotow(Func<IRobot> robotBuild, Func<IRobot> robotLatajacyBuild) { this.robotBuild = robotBuild; this.robotLatajacyBuild = robotLatajacyBuild; } public IRobot ZbudujRobota() { bool decyzja = new Random().Next(1) == 0; if (decyzja) return robotBuild(); else return robotLatajacyBuild();
30
Użycie fabryki class ApplicationInjector { public MainHelper InjectMainHelper(ApplicationScope scope) { return new MainHelper( scope.Args, InjectFabrykaRobotow(scope).ZbudujRobota()); } private FabrykaRobotow InjectFabrykaRobotow(ApplicationScope scope) { return new FabrykaRobotow( () => InjectRobot(scope), () => InjectRobotLatajacy(scope)); private IRobot InjectRobot(ApplicationScope scope) { /*…*/ } private IRobot InjectRobotLatajacy(ApplicationScope scope) { /*…*/ }
31
Uchwyty do obiektów w Scope
32
Uchwyt do obiektu w Scope
class ScopeCache<T> { private readonly = new object(); private volatile bool full; private T cache; public T Get(Func<T> initiator) { if (!full) { lock { if (!full) { cache = initiator(); full = true; } return cache;
33
Użycie ScopeCache w Scope
class ApplicationScope { //… public readonly ScopeCache<FabrykaRobotow> FabrykaRobotowCache = new ScopeCache<FabrykaRobotow>(); }
34
Inicjacja obiektu w Injector
class ApplicationInjector { //… private FabrykaRobotow InjectFabrykaRobotow(ApplicationScope scope) return scope.FabrykaRobotowCache.Get(() => new FabrykaRobotow( () => InjectRobot(scope), () => InjectRobotLatajacy(scope) ) ); }
35
Uchwyt „per-wątek” public class ScopeThreadCache<T> { private readonly ThreadLocal<T> cache = new ThreadLocal<T>(); public T Get(Func<T> initiator) if (!cache.IsValueCreated) cache.Value = initiator(); } return cache.Value;
36
Jeszcze raz: konsekwencja w stosowaniu DI.
37
Mniej lub prostsze Mock’i w testach jednostkowych.
38
Problem z kontekstami.
39
Robot - wywołanie class ApplicationInjector { //…
private IRobot InjectRobot(ApplicationScope scope) return new Robot(scope.MaxX, scope.MaxY); }
40
Robot – definicja klasy
public class Robot : IRobot { private readonly int maxX; private readonly int maxY; internal Robot(int maxX, int maxY) { this.maxX = maxX; this.maxY = maxY; } public bool IdzDo(int x, int y) { // zrób co trzeba return true;
41
Jeżeli parametrów było by zbyt wiele
public class Robot : IRobot { private readonly RobotLimits limits; internal Robot(RobotLimits limits) this.limits = limits; } //… public class RobotLimits public int MaxX { get; set; } public int MaxY { get; set; }
42
Komponenty aplikacji mogą mieć własną parę klas Scope-Injector.
43
Para Scope – Injector dla Komponentu
class ComponentScope { internal readonly ScopeCache<KlientSmtp> KlientSmtpCache = new ScopeCache<KlientSmtp>(); internal readonly ScopeCache<KlientTcp> KlientTcpCache = new ScopeCache<KlientTcp>(); } class ComponentInjector public IKlientSmtp InjectKlientSmtp(ComponentScope scope) { return scope.KlientSmtpCache.Get(() => new KlientSmtp(InjectKlientTcp(scope))); private IKlientTcp InjectKlientTcp(ComponentScope scope) { return scope.KlientTcpCache.Get(() => new KlientTcp());
44
Podpięcie komponentu w ApplicationScope
class ApplicationScope { //… public readonly ScopeCache<ComponentInjector> ComponentInjectorCache = new ScopeCache<ComponentInjector>(); public readonly ScopeCache<ComponentScope> ComponentScopeCache = new ScopeCache<ComponentScope>(); }
45
Użycie komponentu w ApplicationInjector
class ApplicationInjector { //… private Mail InjectMail(ApplicationScope scope) { return new Mail(InjectKlientSmtp(scope)); } private IKlientSmtp InjectKlientSmtp(ApplicationScope scope) { return InjectComponentInjector(scope) .InjectKlientSmtp(InjectComponentScope(scope)); private ComponentInjector InjectComponentInjector(ApplicationScope scope) { return scope.ComponentInjectorCache.Get(() => new ComponentInjector()); private ComponentScope InjectComponentScope(ApplicationScope scope) { return scope.ComponentScopeCache.Get(() => new ComponentScope());
46
Pytania?
47
Źródła testy wydajnościowe kontenerów
Podobne prezentacje
© 2024 SlidePlayer.pl Inc.
All rights reserved.