
Mnemonik SOLID - D jak Dependency Inversion Principle
Przyszła pora na ostatnią literkę z mnemonika SOLID, ale to wcale nie oznacza, że jest ona najmniej ważna. D rozwijane jest jako Dependency Inversion Principle co tłumaczone jest jako Zasada Odwrócenia Zależności. Nazwa brzmi odrobinę tajemniczo, ale tak naprawdę opisuje ona dwie istotne reguły:
- Moduły wysokiego poziomu nie powinny zależeć od modułów niskiego poziomu.
- Abstrakcje nie powinny zależeć od szczegółów (konkretnej implementacji). Z kolei szczegóły (implementacja) powinny zależeć od abstrakcji.
Obie reguły brzmią w chwili obecnej odrobinę tajemniczo, ale jak zobaczycie w dalszej części tekstu, niosą one ze sobą głębszy sens. Dependency Inversion Principle Z pewnością niejednokrotnie programując, zdarzyło Wam się wygenerować kod, który posiadał sztywne powiązania. Np. utworzyliście klasę zależną do klasy głównej, ale jednocześnie nie wyprowadziliście interfejsu klasy podrzędnej. Typowy problem każdego programisty, który wraz ze wzrostem doświadczenia zdarza się coraz rzadziej. Okazuje się jednak, że samo stworzenie interfejsu czasem nie wystarczy by można było odtrąbić sukces. Niezwykle ważne bywa również miejsce, w którym się go tworzy oraz szersza perspektywa. Tworząc interfejs powinniśmy myśleć przede wszystkim o klasie głównej, która będzie go konsumowała, a nie o klasie szczegółu, która będzie go implementowała. Trzymając się standardowej nomenklatury dla tego przypadku, wyjściowo mamy klasy: - Foo - Bar - Bar jest elementem podrzędnym dla Foo Już na pierwszy rzut oka widoczne jest sztywne powiązanie pomiędzy klasami. Naturalnym krokiem będzie wydzielenie interfejsu IBar. W tym miejscu jednak powinniśmy pamiętać, by przede wszystkim zaspokajał on potrzeby klasy Foo, a dopiero później potrzeby klasy Bar, którą trzeba by ewentualnie dostosować do nowych realiów: - Foo - IBar - Bar - Foo wykorzystuje interfejs IBar, Bar implementuje interfejs IBar Można to schematycznie przedstawić za pomocą diagramu, na którym widać wyraźny podział pomiędzy elementami, w którym to klasa Foo i interfejs IBar trzymają się razem, natomiast klasa szczegółów Bar, znajduje się gdzieś „na uboczu”. Przekładając to na biblioteki, bardzo często będzie tak, że klasa Foo oraz interfejs IBar znajdą się w jednej z bibliotek, natomiast konkretna implementacja pojawi się w innym miejscu. Za pomocą kontenera IoC dokonamy wiązania interfejsu oraz konkretnej implementacji klasy szczegółu w miejscu wdrożenia. Przykład praktyczny W przykładzie praktycznym skorzystam raz jeszcze z kodu generatora raportów. Przypomnijmy sobie najpierw strukturę klasy ReportGenerator, którą możemy uznać za główną w naszym obecnym przypadku: [sourcecode language="csharp"] public class ReportGenerator { public void GenerateReport(IFileWriter fileWriter, DateTime date) { IDatabaseManager databaseManager = new MyDatabaseManager(); var reportData = databaseManager.GetReportData(date); fileWriter.Open(); fileWriter.WriteData(reportData); fileWriter.Close(); } } [/sourcecode] Wcześniej pomyślałem już o odpowiednich interfejsach, dlatego część problemu mamy z głowy. Raz jeszcze musimy pamiętać teraz, by odwrócić zależności tj. interfejsy powinny być przede wszystkim ważne z perspektywy potrzeb klasy ReportGenerator, a nie z perspektywy klas, które implementują interfejsy IDatabaseManager oraz IFileWriter. Bardzo dobrym posunięciem byłoby w tym przypadku nawet, przesunięcie klas implementacyjnych do osobnych bibliotek (tego oczywiście nie będzie widać na listingu, ale pozostawiam to Waszej wyobraźni:-). Taki krok pozwoliłby na lepsze pogłębienie zasady Dependency Inversion Principle i jednocześnie przyczyniłby się do zwiększenia użyteczności biblioteki bazowej. Jeśli kiedykolwiek wykorzystywaliście biblioteki portable (a teraz również .Net Core) to szybko przekonacie się, że im mniej kodu, w tak kluczowych miejscach tym lepiej. Trudniej w takiej sytuacji natknąć na kod specyficzny dla danej platformy. Na koniec przypomnę również definicję wymienionych wyżej interfejsów: [sourcecode language="csharp"] public interface IFileWriter { void Open(); void WriteData(IEnumerable
Zobacz nasze propozycje
-
(druk)
(41.40 zł najniższa cena z 30 dni)
41,40 zł69,00 zł -
Rozmowa kwalifikacyjna. O czym nie wiedzą kandydaci do pracy, czyli sekrety rekrutujących. Wydanie 5(druk)
(28.20 zł najniższa cena z 30 dni)
28,20 zł47,00 zł -
(47.40 zł najniższa cena z 30 dni)
47,40 zł79,00 zł -
(53.40 zł najniższa cena z 30 dni)
53,40 zł89,00 zł -
(65.40 zł najniższa cena z 30 dni)
65,40 zł109,00 zł -
(41.40 zł najniższa cena z 30 dni)
41,40 zł69,00 zł -
(22.20 zł najniższa cena z 30 dni)
22,20 zł37,00 zł -
(41.40 zł najniższa cena z 30 dni)
41,40 zł69,00 zł -
(druk)
(77.35 zł najniższa cena z 30 dni)
71,40 zł119,00 zł -
(59.40 zł najniższa cena z 30 dni)
59,40 zł99,00 zł -
(47.40 zł najniższa cena z 30 dni)
47,40 zł79,00 zł -
(druk)
(57.85 zł najniższa cena z 30 dni)
53,40 zł89,00 zł -
(druk)
(41.40 zł najniższa cena z 30 dni)
41,40 zł69,00 zł -
(40.20 zł najniższa cena z 30 dni)
40,20 zł67,00 zł -
(46.20 zł najniższa cena z 30 dni)
46,20 zł77,00 zł -
(59.40 zł najniższa cena z 30 dni)
59,40 zł99,00 zł -
(41.40 zł najniższa cena z 30 dni)
41,40 zł69,00 zł -
(druk)
(47.40 zł najniższa cena z 30 dni)
47,40 zł79,00 zł -
(53.40 zł najniższa cena z 30 dni)
53,40 zł89,00 zł -
(24.95 zł najniższa cena z 30 dni)
29,94 zł49,90 zł