1 / 47

DIY - DI

Adam Suwała. DIY - DI. Agenda:. Dependency Injection - trochę podstaw Kontenery DI - masz problem, weź framework Service Locator - anti-pattern Do-It-Yourself Dependency Injection. Dependency Injection - trochę podstaw. Definicje z WIKI.

sef
Download Presentation

DIY - DI

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Adam Suwała DIY - DI

  2. Agenda: • DependencyInjection - trochę podstaw • Kontenery DI - masz problem, weź framework • Service Locator - anti-pattern • Do-It-YourselfDependencyInjection

  3. DependencyInjection - trochę podstaw

  4. Definicje z WIKI •Wstrzykiwanie zależności (DependencyInjection, 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” (loosecoupling). • ułatwić testy jednostkowe kodu (automatyczne testy pojedynczych klas w izolacji od innych)

  6. Rodzaje wstrzykiwania zależności: • ConstructorInjection • SetterInjection • InterfaceInjection

  7. Jak to wygląda w praktyce? Tradycyjne podejście: class Mail { //… public boolWyslij() { varklientSmtp = newKlientSmtp(); return klientSmtp.Wyslij(adres, tytul, tresc); } //… }

  8. Podejście DI: class Mail { privatereadonlyIKlientSmtpklientSmtp; public Mail(IKlientSmtpklientSmtp) { this.klientSmtp = klientSmtp; } //… public boolWyslij() { varklientSmtp = newKlientSmtp(); return klientSmtp.Wyslij(adres, tytul, tresc); } //… }

  9. Skąd w DI biorą się zależności? class Mail { privatereadonlyIKlientSmtpklientSmtp; public Mail(IKlientSmtpklientSmtp) { 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 classKlientTcp : IKlientTcp { /*…*/ } classKlientSmtp : IKlientSmtp { privatereadonlyIKlientTcpklientTcp; public KlientSmtp(IKlientTcpklientTcp) { this.klientTcp = klientTcp; } /*…*/ } class Mail { privatereadonlyIKlientSmtpklientSmtp; public Mail(IKlientSmtpklientSmtp) { this.klientSmtp = klientSmtp; } /*…*/ }

  15. Kaskada zależności – jak złożyć? classUzycieKontenera { voidPrzyklad() { 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 boolWyslij() { varklientSmtp = Kontener.Resolve<IKlientSmtp>(); return klientSmtp.Wyslij(adres, tytul, tresc); } //… }

  21. Do-It-YourselfDependencyInjection

  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 classApplicationScope { privatereadonlystring[] args; public ApplicationScope(string[] args) { this.args = args; MaxX = 100; MaxY = 100; } public string[] Args { get { return args; } } public intMaxX { get; private set; } public intMaxY { get; private set; } }

  25. MainHelper – „biznesowo-pomocnicza” klasa, która umożliwia wstrzykiwanie zależności od samego startu programu classMainHelper { privatereadonlystring[] args; privatereadonlyIRobot 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 classApplicationInjector { public MainHelperInjectMainHelper(ApplicationScopescope) { return newMainHelper(scope.Args, InjectRobot(scope)); } privateIRobotInjectRobot(ApplicationScopescope) { return new Robot(scope.MaxX, scope.MaxY); } }

  27. Start programu class Program { staticvoidMain(string[] args) { varscope = newApplicationScope(args); newApplicationInjector() .InjectMainHelper(scope) .Run(); } }

  28. Co zrobić ze skomplikowanymi przypadkami?

  29. Fabryka – ale nie taka zwyczajna public classFabrykaRobotow { privatereadonlyFunc<IRobot> robotBuild; privatereadonlyFunc<IRobot> robotLatajacyBuild; internalFabrykaRobotow(Func<IRobot>robotBuild, Func<IRobot> robotLatajacyBuild) { this.robotBuild = robotBuild; this.robotLatajacyBuild = robotLatajacyBuild; } public IRobotZbudujRobota() { bool decyzja = new Random().Next(1) == 0; if (decyzja) returnrobotBuild(); else return robotLatajacyBuild(); } }

  30. Użycie fabryki classApplicationInjector { public MainHelperInjectMainHelper(ApplicationScopescope) { return newMainHelper( scope.Args, InjectFabrykaRobotow(scope).ZbudujRobota()); } privateFabrykaRobotowInjectFabrykaRobotow(ApplicationScopescope) { return newFabrykaRobotow( () => InjectRobot(scope), () => InjectRobotLatajacy(scope)); } privateIRobotInjectRobot(ApplicationScopescope) { /*…*/ } privateIRobotInjectRobotLatajacy(ApplicationScopescope) { /*…*/ } }

  31. Uchwyty do obiektów w Scope

  32. Uchwyt do obiektu w Scope classScopeCache<T> { privatereadonlyobject @lock = newobject(); privatevolatileboolfull; private T cache; public T Get(Func<T> initiator) { if (!full) { lock (@lock) { if (!full) { cache = initiator(); full = true; } } } return cache; } }

  33. Użycie ScopeCache w Scope classApplicationScope { //… public readonlyScopeCache<FabrykaRobotow>FabrykaRobotowCache = newScopeCache<FabrykaRobotow>(); //… }

  34. Inicjacja obiektu w Injector classApplicationInjector { //… privateFabrykaRobotowInjectFabrykaRobotow(ApplicationScopescope) { return scope.FabrykaRobotowCache.Get(() => newFabrykaRobotow( () => InjectRobot(scope), () => InjectRobotLatajacy(scope) ) ); } //… }

  35. Uchwyt „per-wątek” public classScopeThreadCache<T> { privatereadonlyThreadLocal<T> cache = newThreadLocal<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 classApplicationInjector { //… privateIRobotInjectRobot(ApplicationScopescope) { return new Robot(scope.MaxX,scope.MaxY); } //… }

  40. Robot – definicja klasy public class Robot : IRobot { privatereadonlyintmaxX; privatereadonlyintmaxY; internal Robot(intmaxX, intmaxY) { this.maxX = maxX; this.maxY = maxY; } public boolIdzDo(int x, int y) { // zrób co trzeba return true; } }

  41. Jeżeli parametrów było by zbyt wiele public class Robot : IRobot { privatereadonlyRobotLimitslimits; internal Robot(RobotLimitslimits) { this.limits = limits; } //… } public classRobotLimits { public intMaxX { get; set; } public intMaxY { get; set; } //… }

  42. Komponenty aplikacji mogą mieć własną parę klas Scope-Injector.

  43. Para Scope – Injector dla Komponentu classComponentScope { internalreadonlyScopeCache<KlientSmtp> KlientSmtpCache = newScopeCache<KlientSmtp>(); internalreadonlyScopeCache<KlientTcp> KlientTcpCache = newScopeCache<KlientTcp>(); } classComponentInjector { public IKlientSmtpInjectKlientSmtp(ComponentScopescope) { return scope.KlientSmtpCache.Get(() => newKlientSmtp(InjectKlientTcp(scope))); } privateIKlientTcpInjectKlientTcp(ComponentScopescope) { return scope.KlientTcpCache.Get(() => newKlientTcp()); } }

  44. Podpięcie komponentu w ApplicationScope classApplicationScope { //… public readonlyScopeCache<ComponentInjector> ComponentInjectorCache = newScopeCache<ComponentInjector>(); public readonlyScopeCache<ComponentScope> ComponentScopeCache = newScopeCache<ComponentScope>(); }

  45. Użycie komponentu w ApplicationInjector classApplicationInjector { //… private Mail InjectMail(ApplicationScopescope) { return new Mail(InjectKlientSmtp(scope)); } privateIKlientSmtpInjectKlientSmtp(ApplicationScopescope) { return InjectComponentInjector(scope) .InjectKlientSmtp(InjectComponentScope(scope)); } privateComponentInjectorInjectComponentInjector(ApplicationScopescope) { return scope.ComponentInjectorCache.Get(() => newComponentInjector()); } privateComponentScopeInjectComponentScope(ApplicationScopescope) { return scope.ComponentScopeCache.Get(() => newComponentScope()); } }

  46. Pytania?

  47. Źródła http://pl.wikipedia.org/wiki/Wstrzykiwanie_zale%C5%BCno%C5%9Bci http://www.martinfowler.com/articles/injection.html http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx http://philipm.at/2011/0808/ --- testy wydajnościowe kontenerów http://misko.hevery.com/2010/05/26/do-it-yourself-dependency-injection/ http://blacksheep.parry.org/wp-content/uploads/2010/03/DIY-DI.pdf

More Related