100915348

Page 1


spis treści

przedmowa xv

podziękowania xvii

o książce xix

o autorach xxv

o ilustracji na okładce xxvii

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

KONSTRUKTOREM 107 ■ Przykład: Dodawanie przeliczania walut do wyróżnionych produktów 108 ■ Podsumowanie 110

4.3. WSTRZYKIWANIE METODĄ 112

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

10 Rozmyślne Programowanie Aspektowe 326

10.1. Wprowadzenie do AOP 327

10.2. Zasady SOLID 330

ZASADA POJEDYNCZEJ ODPOWIEDZIALNOŚCI (SRP) 331 ■ ZASADA

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

13.2. Zarządzanie cyklem życia 437

Konfigurowanie zakresów instancji 438 ■ Zwalnianie komponentów 440

13.3. Rejestrowanie trudnych API 443

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ą

this.repository = repository; this.service = service; }

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

public interface IImageEffectAddIn { Bitmap Apply( Bitmap source, IApplicationContext context); }

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.

Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.
100915348 by WN PWN - Issuu