Część 1 UmiejsCowienie wstrzykiwania zależnośCi na mapie ................... 1
1 Podstawy Wstrzykiwania zależności: co, jak i dlaczego 3
1.1. Pisanie utrzymywalnego kodu 5
Powszechne mity na temat DI 6 ■ Rozumienie celu DI 9
1.2. Prosty przykład: Hello DI! 16
Kod Hello DI! 16 ■ Korzyści (płynące) z DI 19
1.3. Co wstrzykiwać, a czego nie wstrzykiwać 28
STABILNE ZALEŻNOŚCI 29 ■ NIESTABILNE ZALEŻNOŚCI 30
1.4. Zakres DI 31
KOMPOZYCJA OBIEKTOWA 32 ■ CYKL ŻYCIA OBIEKTU 33 ■ PRZECHWYTYWANIE 34 ■ DI w trzech wymiarach 35
1.5. Konkluzje 36
2 Pisanie ściśle powiązanego kodu 39
2.1. Budowanie ściśle powiązanej aplikacji 40
Poznajmy Mary Rowan 41 ■ Tworzenie warstwy danych 42 ■ Tworzenie warstwy domeny 44 ■ Tworzenie warstwy interfejsu użytkownika 48
2.2. Ocena aplikacji ściśle powiązanej 50
Ocena grafu zależności 50 ■ Ocena tworzenia strukturalności 50
2.3. Analiza braku strukturalności 53
Analiza wykresu zależności 53 ■ Analiza interfejsu dostępu do danych 54 ■ Inne kwestie 56
2.4. Konkluzja 57
3 Pisanie luźno powiązanego kodu 59
3.1. Ponowne budowanie aplikacji e-commerce 60
Budowanie bardziej utrzymywanego UI 63 ■ Budowanie niezależnego modelu domeny 69 ■ Budowanie nowej warstwy dostępu do danych 78 ■ Implementacja Adaptera IUserContext specyficznego dla ASP.NET Core 79 ■ Tworzenie aplikacji w PODSTAWIE KOMPOZYCJI 82
3.2. Analiza luźno powiązanej implementacji 82
Zrozumienie interakcji między komponentami 83 ■ Analizowanie nowego grafu zależności 84
4 Wzorce DI 91
4.1. PODSTAWA KOMPOZYCJI 93
Jak działa PODSTAWA KOMPOZYCJI 95 ■ Użycie KONTENERA DI w PODSTAWIE KOMPOZYCJI 96 ■ Przykład: Implementowanie PODSTAWY KOMPOZYCJI przy użyciu CZYSTEGO DI 97 ■ Pozorna eksplozja zależności 100
4.2. WSTRZYKIWANIE KONSTRUKTOREM 102
Jak działa WSTRZYKIWANIE KONSTRUKTOREM 102 ■ Kiedy używać WSTRZYKIWANIA KONSTRUKTOREM 104 ■ Znany sposób użycia WSTRZYKIWANIA
Jak działa WSTRZYKIWANIE METODĄ 112 ■ Kiedy używać WSTRZYKIWANIA METODĄ 113 ■ Znany sposób użycia WSTRZYKIWANIA METODĄ 119
■ Przykład: Dodawanie przewalutowania do ENCJI Product 121
4.4. WSTRZYKIWANIE WŁAŚCIWOŚCIĄ 123
Jak działa WSTRZYKIWANIE WŁAŚCIWOŚCIĄ 124 ■ Kiedy używać WSTRZYKIWANIA WŁAŚCIWOŚCIĄ 125 ■ Znane zastosowania WSTRZYKIWANIA WŁAŚCIWOŚCIĄ 128 ■ Przykład: WSTRZYKIWANIE WŁAŚCIWOŚCIĄ jako model rozszerzalności biblioteki do ponownego użycia 128
4.5. Wybieranie wzorca (do pracy) 131
5 Antywzorce DI 135
5.1. ANTYWZORZEC CONTROL FREAK 138
Przykład: CONTROL FREAK przez nowo powstające ZALEŻNOŚCI 139 ■ Przykład: CONTROL FREAK przez fabryki 140 ■ Przykład: CONTROL FREAK przez przeciążone konstruktory 146 ■ Analiza CONTROL FREAK 147
5.2. LOKALIZATOR USŁUG 149
Przykład: ProductService przy użyciu LOKALIZATORA USŁUG 151 ■ Analiza LOKALIZATORA USŁUG 154
5.3. KONTEKST OTOCZENIA 158
Przykład: Dostęp do czasu przez KONTEKST OTOCZENIA 159 ■ Przykład: Logowanie przez KONTEKST OTOCZENIA 161 ■ Analiza KONTEKSTU OTOCZENIA 162
5.4. Antywzorzec OGRANICZONEJ KONSTRUKCJI 166
Przykład: Późne wiązanie ProductRepository 167 ■ Analiza OGRANICZONEJ KONSTRUKCJI 169
6 Zapachy kodu 176
6.1. Radzenie sobie z zapachem kodu Przesadnego Wstrzykiwania Konstruktorem 177
Rozpoznawanie Przesadnego Wstrzykiwania Konstruktorem 179 ■ Refaktorowanie z Przesadnego Wstrzykiwania Konstruktorem do Usług fasadowych 182 ■ Refaktorowanie z Przesadnego Wstrzykiwania Konstruktorem do zdarzeń domeny 187
6.2. Nadużywanie Fabryk abstrakcyjnych 195 Nadużywanie Fabryk abstrakcyjnych do pokonania problemów cyklu życia 195 ■ Nadużywanie Fabryk abstrakcyjnych do wybierania ZALEŻNOŚCI na podstawie danych czasu wykonania 203
6.3. Naprawianie cyklicznych ZALEŻNOŚCI 209
Przykład: Cykliczne ZALEŻNOŚCI spowodowane naruszeniem SRP 211 ■ Analiza cyklu ZALEŻNOŚCI Mary 215 ■ Refaktoring naruszeń SRP, aby rozwiązać cykl ZALEŻNOŚCI 216 ■ Powszechne strategie przerywania cyklów ZALEŻNOŚCI 220 ■ Ostateczność: przerwanie cyklu za pomocą WSTRZYKIWANIA WŁAŚCIWOŚCIĄ 220
Część 3
7 Kompozycja aplikacji 227
7.1. Komponowanie aplikacji konsolowej 230
Przykład: Uaktualnienie kursu walut przy użyciu programu UpdateCurrency 230 ■ Budowanie PODSTAWY KOMPOZYCJI programu UpdateCurrency 231 ■ Komponowanie grafów obiektów w CreateCurrencyParser 232 ■ Głębsze spojrzenie na warstwy UpdateCurrency 233
7.2. Komponowanie aplikacji UWP 234
Kompozycja UWP 235 ■ Przykład: Podłączanie aplikacji typu gruby klient do zarządzania produktami 235 ■ Implementowanie PODSTAWY KOMPOZYCJI w aplikacji UWP 243
7.3. Komponowanie aplikacji ASP.NET Core MVC 246
Tworzenie własnego aktywatora kontrolera 247 ■ Konstruowanie własnej warstwy middleware przy użyciu CZYSTEGO DI 250
8 Cykl życia obiektu 254
8.1. Zarządzanie CYKLEM ŻYCIA ZALEŻNOŚCI 256
Wprowadzenie ZARZĄDZANIA CYKLEM ŻYCIA 257 ■ Zarządzanie cyklem życia przy użyciu CZYSTEGO DI 261
8.2. Pracowanie z usuwalnymi ZALEŻNOŚCIAMI 264
Konsumowanie usuwalnych ZALEŻNOŚCI 265 ■ Zarządzanie usuwalnymi ZALEŻNOŚCIAMI 269
8.3. Katalog STYLÓW ŻYCIA 275
STYL ŻYCIA SINGLETON 276 ■ PRZEJŚCIOWY STYL ŻYCIA 279 ■ ZAKRESOWY STYL ŻYCIA 281
8.4. Złe wybory STYLU ŻYCIA 287
POJMANE ZALEŻNOŚCI 288 ■ Wybór STYLU ŻYCI A wyciekając y do klas konsumujących przez CIEKNĄCE ABSTRAKCJE 290 ■ Powodowanie błędów współbieżności przez łączenie instancji z cyklem życia jednego wątku 297
9 Przechwytywanie 303
9.1. Przedstawienie PRZECHWYTYWANIA 305
Wzorzec projektowy dekorator 306 ■ Przykład: Implementowanie audytowania przy użyciu Dekoratora 311
9.2. Implementowanie ZAGADNIEŃ PRZEKROJOWYCH 314
Przechwytywanie za pomocą wzorca Bezpiecznik 315 ■ Raportowanie wyjątków przy użyciu wzorca Dekorator 321 ■ Zapobieganie nieautoryzowanemu dostępowi do wrażliwej funkcjonalności przy użyciu Dekoratora 323
OTWARTE/ZAMKNIĘTE (OCP) 332 ■ ZASADA PODSTAWIENIA LISKOV (LSP) 332 ■ ZASADA SEGREGACJI INTERFEJSÓW (ISP) 333 ■ ZASADA ODWRÓCENIA ZALEŻNOŚCI (DIP) 333 ■ Zasady SOLID a PRZECHWYTYWANIE 333
10.3. SOLID jako sterownik dla AOP 334
Przykład: Implementowanie funkcjonalności związanych z produktem przy użyciu IProductService 334 ■ Analiza IProductService z perspektywy SOLID 336 ■ Ulepszanie projektu przez zastosowanie zasad SOLID 339 ■ Dodanie większej liczby ZAGADNIEŃ PRZEKROJOWYCH 353 ■ Wnioski 363
11 Programowanie Aspektowe oparte na narzędziach 367
11.1. Dynamiczne PRZECHWYTYWANIE 368
Przykład: PRZECHWYTYWANIE za pomocą Castle Dynamic Proxy 371 ■ Analiza dynamicznego PRZECHWYTYWANIA 373
11.2. Tkanie w czasie kompilacji 375
Przykład: Zastosowanie aspektu transakcji przy użyciu tkania w czasie kompilacji 377 ■ Analiza tkania w czasie kompilacji 379
4
12 Wprowadzenie do Kontenera DI 387
12.1. Wprowadzenie do KONTENERÓW DI 389
Odkrywanie API kontenerów rozwiązującego ZALEŻNOŚCI 389 ■ AUTOMATYCZNE PODŁĄCZANIE 392 ■ Przykład: Implementowanie uproszczonego KONTENERA DI, który wspiera AUTOMATYCZNE PODŁĄCZANIE 393
12.2. Konfigurowanie KONTENERÓW DI 401
Konfigurowanie kontenerów za pomocą plików konfiguracyjnych 403 ■ Kontenery konfiguracyjne przy użyciu KONFIGURACJI JAKO KODU 406
■ Konfigurowanie kontenerów przez konwencję przy użyciu AUTO-REJESTRACJI 409 ■ Mieszanie i dopasowywanie podejść konfiguracji 415
12.3. Kiedy używać KONTENERA DI 416
Używanie bibliotek trzecich oznacza koszty i ryzyko 416 ■ CZYSTE DI daje krótszy cykl feedbacku 419 ■ Werdykt: kiedy używać KONTENERA DI 420
13 Kontener DI Autofac 425
13.1. Wprowadzenie do Autofac 426
Rozwiązywanie obiektów 427 ■ Konfigurowanie ContainerBuilder 430
Konfigurowanie prymitywnych ZALEŻNOŚCI 443 ■ Rejestrowanie obiektów za pomocą bloków kodu 445
13.4. Praca z licznymi komponentami 447
Wybieranie wśród wielu kandydatów 447 ■ Podłączenie sekwencji 452 ■ Podłączanie Dekoratorów 455 ■ Pisanie Kompozytów 458
14 Kontener DI Simple Injector 463
14.1. Wprowadzenie do Simple Injector 464
Tworzenie obiektów 466 ■ Konfigurowanie kontenera 468
14.2. Zarządzanie cyklem życia 476
Konfigurowanie STYLÓW ŻYCIA 477 ■ Zwalnianie komponentów 478 ■ Zakresy otoczenia 481 ■ Diagnozowanie kontenera pod kątem częstych problemów cyklu życia 483
14.3. Rejestrowanie trudnych API 486
Konfigurowanie prymitywnych ZALEŻNOŚCI 486 ■ Wyciąganie prymitywnych ZALEŻNOŚCI do Obiektów Parametrów 488 ■ Rejestrowanie obiektów za pomocą bloków kodu 489
14.4. Praca z licznymi komponentami 490
Wybieranie spośród licznych kandydatów 491 ■ Podłączanie sekwencji 494 ■ Podłączanie Dekoratorów 497 ■ Podłączanie Kompozytów 500 ■ Sekwencje są strumieniami 502
15 Kontener DI Microsoft.Extensions.DependencyInjection 507
15.1. Wprowadzenie do Microsoft.Extensions.DependencyInjection 508
Rozwiązywanie obiektów 510 ■ Konfigurowanie ServcieCollection 512
15.2. Zarządzanie cyklem życia 518
Konfigurowanie STYLÓW ŻYCIA 519 ■ Zwalnianie komponentów 519
15.3. Rejestrowanie trudnych API 522
Konfigurowanie prymitywnych Z ALEŻNOŚCI 522 ■ Wyciąganie prymitywnych ZALEŻNOŚCI do Obiektów Parametru 523 ■ Rejestrowanie obiektów za pomocą bloków kodu 524
15.4. Praca z licznymi komponentami 526
Wybieranie wśród licznych kandydatów 526 ■ Podłączanie sekwencji 530 ■ Podłączanie Dekoratorów 533 ■ Podłączanie Kompozytów 537
słowniczek terminologii 545
literatura 549
Pozycje drukowane 549
Pozycje online 550
Inne źródła 551
indeks 553
2Pisanie ściśle powiązanego kodu
W rozdziale:
¡ Pisanie ściśle powiązanej aplikacji
¡ Ocena strukturalności aplikacji
¡ Analiza braku strukturalności w aplikacji
Tak, jak wspomnieliśmy o tym w rozdziale 1, sos berneński to zemulgowany sos na bazie żółtek i masła, ale ta wiedza nie powoduje, że w magiczny sposób ktoś potrafi przygotować taki dodatek do dań. Najlepszym sposobem, by się tego nauczyć, jest próbowanie, ale często jeden przykład może przybliżyć pustą przestrzeń między teorią a praktyką oraz pozbyć się jej. Oglądanie profesjonalnego kucharza przygotowującego sos berneński jest pomocne, zanim samemu wypróbuje się przygotowanie własnej wersji.
Kiedy wprowadziliśmy Wstrzykiwanie zależności we wcześniejszym rozdziale, zabraliśmy czytelników na przejażdżkę na wysokim poziomie, żeby każdy zrozumiał cel i ogólne zasady DI . Ale to proste wytłumaczenie nie oddaje sprawiedliwości Wstrzykiwaniu zależności. DI to sposób na włączenie luźnego wiązania, a luźne wiązanie to przede wszystkim skuteczny sposób na poradzenie sobie ze złożonością.
Większość oprogramowania jest tak złożona, że musi adresować wiele kwestii jednocześnie. Oprócz zagadnień biznesowych, które mogą być kompleksowe same w sobie, oprogramowanie musi również rozwiązywać kwestie związane z bezpieczeństwem, diagnostyką, operacjami, wydajnością i rozszerzalnością. Zamiast odnosić się do wszystkich tych zagadnień w jednej wielkiej kuli błota (a big ball of mud), luźne wiązanie zachęca do odnoszenia się do każdego zagadnienia osobno. Łatwiej robić to w odizolowaniu, ale ostatecznie i tak aplikacja musi zostać zbudowana ze złożonego zbioru tych zagadnień.
W tym rozdziale przyjrzymy się bardziej złożonemu przykładowi. Pokażemy, jak łatwo napisać ściśle powiązany kod. Wraz z nami będzie można przeanalizować, dlaczego taki ściśle powiązany kod jest problematyczny z perspektywy utrzymywalności. W rozdziale 3 użyjemy DI do całkowitego przepisania ściśle powiązanego kodu na taki, który jest luźno powiązany. Jeśli ktoś chce przyjrzeć się temu drugiemu kodowi od razu, może pominąć rozdział 3. A jeśli nie, kiedy skończy czytać o tym zagadnieniu, powinien zacząć rozumieć, co sprawia, że ściśle powiązany kod jest tak problematyczny.
2.1. Budowanie ściśle powiązanej aplikacji
Pomysł budowania luźno powiązanego kodu nie jest szczególnie kontrowersyjnym pomysłem, ale istnieje duża dysproporcja między teorią a praktyką. Zanim pokażemy w następnym rozdziale, jak użyć DI do zbudowania luźno powiązanej aplikacji, chcemy wskazać, jak łatwo może to pójść nie po czyjejś myśli. Częstym podejściem do luźno powiązanego kodu jest budowanie aplikacji złożonej z warstw. Każdy może narysować diagram trójwarstwowej aplikacji, a rys. 2.1 dowodzi, że i my to potrafimy.
Warstwa
UI/interfejsu użytkownika
Warstwa domeny
Warstwa dostępu do danych
Rysunek 2.1. standardowa architektura trójwarstwowej aplikacji. Jest to najprostszy i najczęściej spotykany wariant architektury n-warstwowej aplikacji, gdzie aplikacja jest stworzona z n różnych warstw
Stworzenie takiego trójwarstwowego diagramu jest pozornie proste, ale samo rysowanie jakiegokolwiek diagramu jest podobne do zażyczenia sobie sosu berneńskiego do steku: jest to deklaracja chęci, która nie niesie za sobą żadnej gwarancji odnośnie do finalnego produktu. Można uzyskać coś innego. Będzie można to zobaczyć już za chwilę.
Istnieje więcej niż jeden sposób patrzenia i tworzenia elastycznej i utrzymywalnej złożonej aplikacji, ale n- warstwowa architektura aplikacji to podejście „wypróbowane i sprawdzone”. Wyzwaniem jest zaimplementowanie jej poprawnie. Będąc uzbrojonym w trójwarstwowy diagram, taki jak ten na rys. 2.1, można zacząć budowanie aplikacji.
public class Customer
{
public Guid Id { get; private set; } public string Name { get; private set; }
public Customer(Guid id, string name) { }
public void RedeemVoucher( Voucher voucher, IVoucherRedemptionService service)
{ if (voucher == null) throw new ArgumentNullException("voucher"); if (service == null) throw new ArgumentNullException("service"); service.ApplyRedemptionForCustomer( voucher, this.Id); }
public void MakePreferred(IEventHandler handler)
Używając wstrzykiwania metodą, obydwie metody domeny encji, RedeemVoucher i MakePreferred, akceptują wymagane zależności –IVoucherRedemptionService i IEventHandler. Weryfikują parametr y i używają dostarczonej zależności
{ if (handler == null) throw new ArgumentNullException("handler"); handler.Publish(new CustomerMadePreferred(this.Id)); } }
Wewnątrz komponentu CustomerServices metoda RedeemVoucher należąca do Customer może być wywołana przez przekazywanie ZALEŻNOŚCI IVoucherRedemtionService wraz z wywołaniem, tak jak pokazano to poniżej.
Listing 4.12. Komponent używający wstrzykiwania metodą do przekazywania zależności
public class CustomerServices : ICustomerServices { private readonly ICustomerRepository repository; private readonly IVoucherRedemptionService service;
public CustomerServices( ICustomerRepository repository, IVoucherRedemptionService service) {
Klasa CustomerServices używa wstrzykiwania konstruktorem, żeby statystycznie zdefiniować jej wymagane zależności. IVoucherRedemptionService jest jedną z tych zależności
Listing 4.11. encja używająca wstrzykiwania metodą
public void RedeemVoucher( Guid customerId, Voucher voucher) { var customer = this.repository.GetById(customerId); customer.RedeemVoucher(voucher, this.service); this.repository.Save(customer); } }
zależność IVoucherRedemptionService jest przekazana do już skonstruowanej encji Customer przy użyciu wstrzykiwania metodą. Customer jest stworzona wewnątrz implementacji ICustomerRepository
Na listingu 4.12 tylko pojedyncza instancja Customer jest pobierana z ICustomerRepository. Ale pojedyncza instancja CustomerServices może być wywoływana w kółko dla wielu różnych consumer i voucher, powodując, że ten sam IVoucherRedepmtionService będzie dostarczany do wielu różnych instancji Customer. Customer jest klasą konsumującą ZALEŻNOŚ ć IVoucherRedemptionService , która nie zmienia się dla różnych instancji klasy Consumer. Jest to podobne do pierwszego przykładu WSTRZYKIWANIA METODĄ pokazanego na listingu 4.9 i metody ApplyDiscountFor omówionej na listingu 3.8. Przypadek odwrotny zdarza się wtedy, gdy różnicuje się ZALEŻNOŚCI przy niezmieniających się konsumentach.
przykład: rÓżnicowanie wstrzykniętej zależności przy każdym wywołaniu metody
Wyobraźmy sobie system rozszerzeń dla graficznej aplikacji do rysowania, gdzie każdy mógłby podłączyć własne efekty przetwarzania obrazu. Zewnętrzne efekty przetwarzania obrazu mogą wymagać informacji na temat kontekstu czasu wykonania, które mogą być przekazane przez aplikację do rozszerzenia odpowiadającego za efekt. Jest to typowy przypadek użycia (dla zastosowania) WSTRZYKIWANIA METODĄ . Możemy zdefiniować następujący interfejs w celu zastosowania tych efektów:
abstrakcja rozszerzenia reprezentuje efekty przetwarzania obrazu. Efekty obrazu mogą być podłączone do aplikacji przez implementację tej abstrakcji
Pozwala rozszerzeniu na zaaplikowanie jego efektu do źródła i następnie zwraca nowy obiekt Bitmap z zastosowanym efektem
Zapewnia informację kontekstową do efektu przetwarzania obrazu pr zez aplikację graficzną przy użyciu wstrzykiwania metodą
ZALEŻNOŚć IApplicationContext z IImageEffectAddIn może różnić się w każdym wywołaniu metody Apply, dostarczając rozszerzeniu informacji na temat kontekstu wywołania efektu. Jakakolwiek klasa implementująca ten interfejs może być użyta jako dodatek. Niektóre implementacje będą się przejmować zmienną context, a inne w ogóle. Klient może użyć listy rozszerzeń przez wywołanie każdego z nich ze źródłem Bitmap i kontekstem do wracającego zagregowanego wyniku, tak jak pokazano to poniżej.
Listing 4.13. Przykładowe rozszerzenia klienta
public Bitmap ApplyEffects(Bitmap source) { if (source == null) throw new ArgumentNullException("source");
Bitmap result = source; foreach (IImageEffectAddIn effect in this.effects) { result = effect.Apply(result, this.context); } return result; }
Prywatne pole effects jest listą instancji IImageEffectAddIn, które pozwala klientowi przejść przez listę w celu wywołania metody Apply na każdym z rozszerzeń. Za każdym razem, gdy metoda Apply jest wywołana na rozszerzeniu, kontekst działania, reprezentowany przez pole context, jest przekazywany jako parametr metody: result = effect.Apply(result, this.context);
Czasami wartość i kontekst działania są zawarte w pojedynczej ABSTRAKCJI , która działa jako kombinacja tych dwóch. Ważną kwestią, o której warto pamiętać, jest to, że tak jak widoczne to było na obu przykładach, ZALEŻNOŚć wstrzykiwana przez WSTRZYKIWANIE METODĄ staje się częścią definicji ABSTRAKCJI. Jest to zazwyczaj pożądane, w przypadku kiedy ZALEŻNOŚ ć zawiera informację czasu wykonania dostarczaną przez jej bezpośrednie wywołania.
W przypadkach gdy ZALEŻNOŚ ć jest szczegółem implementacji dla wywołania, powinno się chronić ABSTRAKCJĘ przed byciem „zanieczyszczoną”; dlatego też WSTRZYKIWANIE KONSTRUKTOREM jest lepszym wyborem. W innych przypadkach łatwo można skończyć na przekazywaniu ZALEŻNOŚCI z góry grafu obiektów aplikacji na sam dół, powodując przy tym szeroko zakrojone zmiany.
Wcześniejsze przykłady pokazały użycie WSTRZYKIWANIA METODĄ spoza PODSTAWY KOMPOZYCJI. Było to celowe. WSTRZYKIWANIE METODĄ nie jest odpowiednie, kiedy używa się go wewnątrz PODSTAWY KOMPOZYCJI. Wewnątrz KOMPOZYCJI WSTRZYKIWANIE METODĄ może inicjować wcześniej skonstruowaną klasę wraz z jej ZALEŻNOŚCIAMI. Jednak zrobienie tego w taki sposób prowadzić może do CZASOWEGO WIĄZANIA i z tego powodu jest to silnie odradzane.
W rozdziale:
Komponowanie aplikacji konsolowych
Komponowanie aplikacji
Universal Windows
Komponowanie aplikacji
7
Kompozycja aplikacji
Gotowanie pysznego posiłku składającego się z kilku dań jest wymagającym przedsięwzięciem, w szczególności jeśli gotujący chce również brać udział w samym jedzeniu. Nie można jeść i gotować w tym samym czasie, a wiele dań wymaga przygotowania tuż przed podaniem. Zawodowi kucharze wiedzą, jak poradzić sobie z wieloma z tych wyzwań. Używają wielu trików, a także stosują ogólną zasadę mise en place, co można luźno przetłumaczyć z francuskiego na wszystko na miejscu1. Wszystko, co może być przygotowane zawczasu jest, no cóż, przygotowane zawczasu. Umyte i pokrojone warzywa, pokrojone mięso, ugotowany wywar, nagrzane piekarniki, ułożone potrzebne narzędzia itd.
1 Francuska wymowa: miz en plas.
Jeśli lody są częścią deseru, można je zrobić dzień wcześniej. Jeśli pierwsze danie składa się z muli, można je oczyścić zawczasu. Nawet tak delikatny składnik jak sos berneński może zostać przygotowany do godziny wcześniej. Kiedy goście są gotowi na jedzenie, tylko ostatnie czynności wymagają wykonania: odgrzanie sosu, wysmażenie mięsa itd. W wielu przypadkach końcowa kompozycja dania nie zajmuje więcej niż od 5 do 10 minut. Rysunek 7.1 ilustruje ten proces.
Rysunek 7.1. Mise en place oznacza przygotowanie wszystkich komponentów posiłku wcześniej, tak by końcowa kompozycja posiłku mogła być wykonana tak szybko i bez wysiłku, jak jest to możliwe
Zasada mise en place jest podobna do tworzenia luźno powiązanej aplikacji przy wykorzystaniu DI. Wszystkie wymagane komponenty można napisać wcześniej i połączyć je wtedy, gdy jest ku temu potrzeba.
PODSTAWA KOMPOZYCJI
Warstwy
Tak jak w przypadku wszystkich analogii, tej możemy użyć tylko w takim stopniu, a potem są już tylko same różnice. W gotowaniu przygotowanie i kompozycja są oddzielone czasem, a w tworzeniu aplikacji separacja pojawia się na poziomie modułów i warstw. Rysunek 7.2 pokazuje, jak połączyć elementy w PODSTAWIE KOMPOZYCJI W czasie uruchomienia najpierw pojawia się KOMPOZYCJA OBIEKTOWA Kiedy graf obiektów zostaje zbudowany, KOMPOZYCJA OBIEKTOWA kończy się, a elementy składowe przejmują kontrolę. W tym rozdziale skupimy się na PODSTAWIE KOMPOZYCJI kilku frameworków aplikacji. W porównaniu do mise en place KOMPOZYCJA OBIEKTOWA nie dzieje się tak późno, jak to możliwe, ale dzieje się w miejscu, gdzie integracja różnych modułów jest wymagana.
Rysunek 7.2. PODSTAWA KOMPOZYCJI składa się ze wszystkich niezależnych modułów aplikacji
DEFINICJA KOMPOZYCJA OBIEKTOWA jest działaniem budowania hierarchii pokrewnych komponentów. Ta kompozycja zachodzi wewnątrz PODSTAWY
KOMPOZYCJI.
KOMPOZYCJA OBIEKTOWA jest podstawą DI i do tego jedną z najłatwiejszych części do zrozumienia. Czytelnicy na pewno wiedzą, jak ją przeprowadzić, ponieważ kompozycja następuje przy każdorazowym stworzeniu instancji obiektów zawierających inne obiekty. W sekcji 4.1 omawialiśmy podstawy tego, jak i kiedy budować aplikację. Ten rozdział nie powiela tych informacji. Zamiast tego chcemy pomóc w adresowaniu niektórych z wyzwań, które mogą pojawić się w trakcie tworzenia obiektów. Wyzwania te nie biorą się z samej KOMPOZYCJI OBIEKTOWEJ, ale z frameworków aplikacji, w których się pracuje. Kwestie mają tendencję do bycia konkretnymi dla każdego frameworka i tak samo jest z ich rozwiązaniami. Z naszego doświadczenia wiem, że wyzwania te stanowią jedne z największych przeszkód przed udanym zastosowaniem DI, a więc skupimy się właśnie na nich. Przez to rozdział ten stanie się mniej teoretyczny, a bardziej praktyczny w porównaniu do wcześniejszych.
WAŻNE Jeśli ktoś chce przeczytać jedynie o zastosowaniu DI do swojego wybranego frameworka, może pominąć tę sekcję w tym rozdziale. Każda sekcja jest stworzona tak, by można je było czytać niezależnie.
Łatwo jest stworzyć całą hierarchię ZALEŻNOŚCI aplikacji, kiedy ma się całkowitą kontrolę nad cyklem życia aplikacji (tak jak w przypadku aplikacji konsolowych). Ale niektóre z frameworków w .NET (na przykład ASP.NET Core) angażują ODWRÓCENIE STEROWANIA, co może czasami utrudnić zastosowanie DI. Zrozumienie SZWÓW każdego z frameworków jest kluczowe w zastosowaniu DI dla tego konkretnego frameworka. W tym rozdziale zbadamy, jak implementować PODSTAWĘ KOMPOZYCJI w najczęściej używanych frameworkach .NET Core.
Każdą sekcję rozpoczniemy od ogólnego wprowadzenia do zastosowania DI w konkretnym frameworku, a następnie pokażemy rozbudowany przykład oparty na przykładzie e-commerce, który pojawia się w większości tej książki. Zaczniemy od najprostszego frameworka odnośnie do zastosowania DI , a potem stopniowo przejdziemy do tych bardziej skomplikowanych. Jak do tej pory najprostszym w aplikowaniu DI jest typ aplikacji konsolowej, a więc omówimy go teraz.
WAŻNE Z jednej strony, niektóre ze starych frameworków .NET (na przykład PowerShell lub starsze wersje ASP.NET Web Forms) są zdecydowanie wrogimi środowiskami do zastosowania DI. Z drugiej strony, nowsze frameworki .NET Core są bardziej przyjazne dla DI. W tej książce głównie skupiamy się na tych nowszych. Jeśli ktoś chce się dowiedzieć, jak aplikować DI do ASP.NET MVC, Web Forms, WCF, WPF lub PowerShell, może znaleźć to w cyfrowej wersji pierwszej edycji tej książki; jest ona dostępna wraz z zakupem tej edycji. Rozdział 7 omawia każdy Framework bardzo szczegółowo.