Czy da się uniknać DI?

0

Czy ktoś może mi odpowiedzieć na pytanie?
How do you keep your classes of functions testable with dependencies which have side effects without DI?

Ostatnio zastanawiałem się nad projektowaniem aplikacji w ten sposób aby uniknąć DI, ale nie mogę znaleźć odpowiedzi na pytanie powyżej.

0

Uniknąć DI czy uniknąć kontenera DI?

16

Bez DI testowalność będzie słaba, ale do DI nie trzeba żadnych frameworków/ bibliotek/ kontenerów/ etc (nawet własnych). Do DI wystarczy słówko new i przekazywanie zależności przez konstruktor.

Jeśli nie wstrzykujesz klasie zależności to ona musi zadbać o nie sama, czyli stworzyć je na podstawie globalnej konfiguracji bądź zdobyć z jakiegoś globalnie dostępnego miejsca. To prowadzi do utraty izolacji testów - każdy test musi poczekać aż zakończą się wcześniejsze (tzn testy muszą być jednowątkowe), by móc poprzestawiać globalny stan na potrzeby testowania. Takie cudowanie jest kiepskim pomysłem, bo można się pogubić w manipulowaniu tym globalnym stanem.

4
InterruptedException napisał(a):

Ostatnio zastanawiałem się nad projektowaniem aplikacji w ten sposób aby uniknąć DI, ale nie mogę znaleźć odpowiedzi na pytanie powyżej.

Przy takim podejściu nie powinno się używać słowa "projektowanie".

2

Da się uniknąć, trzeba mieć tylko jednego dobrego zawodnika który będzie ratował projekt.
Przykład: https://denisa1956.files.wordpress.com/2007/09/dsc01395.jpg
Inne pomysły: https://blog.codinghorror.com/the-big-ball-of-mud-and-other-architectural-disasters/

1

How do you keep your classes of functions testable with dependencies which have side effects without DI?

Przychodzi mi na mysł jakiś monkey patching zależności czy podmianki modułów (tak, żeby zamiast normalnych modułów importował zmockowaną zależność), ale nie są to eleganckie sposoby. To raczej w sytuacjach, kiedy miałbyś jakiś legacy kod, który ciężko byłoby jakkolwiek ruszyć i jedynym sposobem na testowanie byłaby ww. partyzantka.

Ew. mógłbyś niczego nie mockować, ale zrobić po prostu test integracyjny. Pewnie nawet lepiej byłoby niż zabawy w mockowanie (które mają to do siebie, że nie są realistyczne).

Natomiast jeśli to twój kod - to kurczę, czemu nie użyć DI? Szczególnie, że tak jak @Wibowit wspomniał - to po prostu przekazywanie zależności przez konstruktor (albo wprost do danej funkcji jako parametr).

Ludzie za duży engineering robią z tego całego DI, a ta cała koncepcja jest prosta jak drut.

6

Pytanie w tym stylu kojarzy mi się tylko z tymi idiotycznymi pytaniami początkujacych z serii chciałbym napisać XYZ ale bez uzycia klas (albo innej konstrukcji języka). Takie rzeczy są po to żeby ułatwić życie a nie je utrudnić :) Niestety są ludzie oporni na wiedzę którzy wolą umieć jak najmniej.

Odpowiedź: da się. Da sie też pisać kod bez znaków nowej linii, albo stosując tylko jednoliterowe identyfikatory. Ale to niestety klasyczny przykład na:

Your scientists were so preoccupied with whether or not they could, they didn't stop to think if they should

1

Najprościej patchować na poziomie kodu źródłowego, jakiś PowerMock modyfikujący klasy binarnie w Javie, Moles/Fakes w dotnecie, w językach dynamicznych jeszcze prościej podmienić implementację obiektu w trakcie. W C/C++ można kombinować nagłówkami, co w gruncie rzeczy jest koncepcyjnie tym samym.

Inne podejście: jeżeli nie wstrzykniesz mocków z zewnątrz, to możesz je zrobić od środka, czyli klasa sama sprawdza, w jakim trybie działa (mock czy produkcja) i w zależności od tego robi coś innego. Pewnie dałoby się to ładnie opakować aspektami, dodać service locator i mieć w miarę poukrywane przełączanie implementacji. Przy odrobinie magii da się nawet równolegle odpalać testy, na przykład przez przepychanie ustawień thread localami.

Jeszcze innym podejściem jest opakowanie zmian stanu (side effect) w zdarzenia, a potem w testach można podmieniać infrastrukturę i gotowe. Czysto teoretycznie każde wywołanie metody można interpretować jako wysłanie wiadomości i czekanie na odpowiedź, możemy zrobić to explicite i dodać mockowanie na poziomie infrastruktury transportującej wiadomości.

0

Jak to mają side efect, niby dlaczego mają mieć side effects. Jak zależności mają side effects to powinno się użyć np. Assercji z "Design By Contract" jeśli oczywiście nie można tego załatwić interfejsem lub inaczej i nie ma to nic wspólnego z DI w testowaniu. Jakieś idiotyczne pytanie...

A niby skont mam wiedzieć, czy ta klasa w ogóle potrzebuje DI, może to nie jest zależność "zewnętrzna"?

Tylko nie mów mi, że to pytanie rekrutacyjne.

Najprościej patchować na poziomie kodu źródłowego, jakiś PowerMock modyfikujący klasy binarnie w Javie, Moles/Fakes w dotnecie, w językach dynamicznych jeszcze prościej podmienić implementację obiektu w trakcie. W C/C++ można kombinować nagłówkami, co w gruncie rzeczy jest koncepcyjnie tym samym.

W .Net możesz użyć nieograniczonego frameworka izolacji, który bazuje na Api profilera - opakowań wokół CLR.

Możesz użyć innego sposobu decoupling'u np. Event'ów.

0

Dzięki za opinie. Przemyślałem temat i przepraszam z góry za niepoprawnie skonstruowane pytanie. Chyba rozbudziło więcej wątpilwości niz powinno.

Generalnie wyjść widzę kilka, przynajmniej tak je widzę:

  1. Używanie DI jako standardowej drogi implementacji IoC.
  2. Mockowanie dużej ilości zależności w testach, bardzo brzydkie rozwiązanie.
  3. Przechylenie się jak najbardziej w kierunku funkcyjnego podejścia. Wtedy nie ma potrzeby żadnego obsługowania side effectów aplikacji. Monady zamiast DI. Generalnie temat bardzo popularny widzę.
    https://earldouglas.com/posts/itof/di-to-reader.html i ku temu będę się składniał.
0
InterruptedException napisał(a):

Dzięki za opinie. Przemyślałem temat i przepraszam z góry za niepoprawnie skonstruowane pytanie. Chyba rozbudziło więcej wątpilwości niz powinno.

Generalnie wyjść widzę kilka, przynajmniej tak je widzę:

  1. Używanie DI jako standardowej drogi implementacji IoC.
  2. Mockowanie dużej ilości zależności w testach, bardzo brzydkie rozwiązanie.
  3. Przechylenie się jak najbardziej w kierunku funkcyjnego podejścia. Wtedy nie ma potrzeby żadnego obsługowania side effectów aplikacji. Monady zamiast DI. Generalnie temat bardzo popularny widzę.
    https://earldouglas.com/posts/itof/di-to-reader.html i ku temu będę się składniał.

1,2 to dalej DI, wiec nie rozumiem jak to się ma do twojego pytanie.

3

Monady? widzę, że @jarekr000000 dobrze hype pompuje, drugi cqrs się robi. :-|

Monada ma inny charakter niż DI. Monadą "nie wstrzykniesz" wartości.

Nie wiem co jest dla ciebie tym side effects.

Wiec ustalmy.

Null nie musi być uznawany za side effects, nie widzę żadnego side effect'u w tym, że coś może być puste. Wyjątek też nie zawsze musi być uznawany za side effect. Wyjątek najczęściej jest, rzucany właśnie po to, żeby uniknąć side effects, w tym celu przerywa się akcje aplikacji wyjątkiem. Jest taki fajny przykład z mieszaniem farb, który tłumaczy side effect, zdaje się, że w książce Evansa.

Nienawidzę jak ktoś mówi mockowanie, zwłaszcza że 93% procent tych ludzi nie rozumie czym mock jest.

2
._. napisał(a):

Monada ma inny charakter niż DI. Monadą "nie wstrzykniesz" wartości.

Nie czytałeś posta na którego odpowiadasz? Przecież In @InterruptedException podał nawet Read Monad, która własnie do tego służy. Do wstrzykiwania zależności. (Co nie znaczy, że często Reader Monad używam.)

Wyjątek też nie zawsze musi być uznawany za side effect.

Tu raczej kwesta u kogo pytasz. Bo zasadniczo jełśi przyjąć, że side effect observable interaction with the outside world besides returning a value to wyjatek rzucony jest side effectem.
Psuje referential transparency.
Ale tutaj sa jakieś kłótnie oczywiście, bo technicznie na prawdziwym metalu w zasadzie wszystkie funkcje mają side effecty. (nawet a+b). I są też tacy, którzy probuja podciągnąć wyjątek pod zwracaną wartość, jest to jednak dość karkołomne. Chyba tylko checked exceptions w javie da się pod taką definicję (na siłę) podciągnąć.

0

Tu raczej kwesta u kogo pytasz. Bo zasadniczo jełśi przyjąć, że side effect observable interaction with the outside world besides returning a value to wyjatek rzucony jest side effectem.

if (value == null)
    throw new exception("Value cant not be null");

Czy to jest dla ciebie jest side effect? Czy zapobiegnięciem side effect'u?

Nie czytałeś posta na którego odpowiadasz? Przecież In @InterruptedException podał nawet Read Monad, która własnie do tego służy. Do wstrzykiwania zależności. (Co nie znaczy, że często Reader Monad używam.)

Nie zrozumieliśmy się Monad może używać DI ale sama koncepcja Monad to co innego niż DI.
On chce zastąpić Monatem DI...

0

@._.:
No w sumie to jest side effect. Zeby takiemu zapobiegać można używać jakichś Optionali albo Either

2

Efekty uboczne a unikanie DI, to dwie różne sprawy. Czym różni się przepychanie readera od samej góry, od przepychania innych zależności? Że już nie wspomnę o tym, że reader najczęściej nie wystarczy, zazwyczaj trzeba opakować go w transformery i liftować masę warstw, co wcale ładne nie jest.

2
._. napisał(a):
if (value == null)
    throw new exception("Value cant not be null");

Czy to jest dla ciebie jest side effect? Czy zapobiegnięciem side effect'u?

Zapobiegnięcie to nie jest na pewno. To jest bardzo ładne zrobienie side effectu. (z niczego :-) ).

Nie zrozumieliśmy się Monad może używać DI ale sama koncepcja Monad to co innego niż DI.
On chce zastąpić Monatem DI...

?? ON po prostu podał konkretny przykład Reader Monad. Który jest własnie formą DI. Nic nie byo o żadnych koncepcjach.

0

Ale po co mam coś zwracać jak chcę zatrzymać działanie funkcji?. To może mieć sens np. przy stringu od tego mam default w .Net

Bo Monady do tego służą. Monada z definicji jest opisem wykonania programu w sposób sekwencyjny, a niektóre z nich oferują możliwość "zatrzymania" działania zgodnie z zasadą "fail-fast"

0

?? ON po prostu podał konkretny przykład Reader Monad. Który jest własnie formą DI. Nic nie byo o żadnych koncepcjach.

Monady zamiast DI. Generalnie temat bardzo popularny widzę.
https://earldouglas.com/posts/itof/di-to-reader.html i ku temu będę się składniał.

Zapobiegnięcie to nie jest na pewno. To jest bardzo ładne zrobienie side effectu. (z niczego :-) ).

Moim zdaniem on tam cały czas był, ja go tylko ujawniłem, rozwiązując problem w mnie modny sposób.

2
._. napisał(a):

Zapobiegnięcie to nie jest na pewno. To jest bardzo ładne zrobienie side effectu. (z niczego :-) ).

Moim zdaniem on tam cały czas był, ja go tylko ujawniłem, rozwiązując problem w mnie modny sposób.

Nic nie ujawniłeś, a jedynie schowałeś. Zwracając Option z funkcji w klarowny sposób mówisz, że funkcja nie musi zwracać wartości - to jest ujawnienie tego co może zrobić funkcja, a jednocześnie poprzez system typów definiujesz constraint. W przypadku exceptionów nie ma żadnych ograniczeń a sygnatura metody nic nie mówi.

0
DisQ napisał(a):
._. napisał(a):

Zapobiegnięcie to nie jest na pewno. To jest bardzo ładne zrobienie side effectu. (z niczego :-) ).

Moim zdaniem on tam cały czas był, ja go tylko ujawniłem, rozwiązując problem w mnie modny sposób.

Nic nie ujawniłeś, a jedynie schowałeś. Zwracając Option z funkcji w klarowny sposób mówisz, że funkcja nie musi zwracać wartości - to jest ujawnienie tego co może zrobić funkcja, a jednocześnie poprzez system typów definiujesz constraint. W przypadku exceptionów nie ma żadnych ograniczeń a sygnatura metody nic nie mówi.

Jeśli kierujesz się myślą im więcje ifów tym lepiej to pewnie masz rację - zwróć optionala.

A kto ci powiedział, że ta wartość coś zwraca?

To może używasz złych nazw metod...

0

Przy Optional nie ma żadnego ifa (jawnego).
Inna kwestia ze nawet jak fukcja zwraca void to może zwracać sukces/porażkę z wytłumaczeniem

0

Przy Optional nie ma żadnego ifa (jawnego).

A co zrobisz z tym optionalem dalej ?
If (optiona.ValueExist) ?

czy
if (optional.Success)?

2

Nie powinno się używać metod isPresent() get(). Raz zapakowanej wartości nie powinno się wprost odpakowywać. (Chyba ze jakiś orElse() albo czymś podobnym, gdzie wiesz co robisz) 
Używasz mormalnie map(),faltMap() żeby operować na wartości. Wtedy jeśli wartość jest to nie ma problemu, jak nie ma to po prostu nic się nie stanie.

0

Moim zdaniem jak użyje try catch to też się nic nie stanie. I lepiej jest to zrobić jakimś aspektem.

A zamiast tych mapów na szczęście mogę sobie napisać w .Net'cie

service.GetUser()?.GetUserGroup()?.GetGroupOwner()

2
._. napisał(a):

Jeśli kierujesz się myślą im więcje ifów tym lepiej to pewnie masz rację - zwróć optionala.

A kto ci powiedział, że ta wartość coś zwraca?

To może używasz złych nazw metod...

Oczywiście nie kieruję się myślą, że im więcej ifów tym lepiej, ba, w przypadku Optional praktycznie nigdy nie powinieneś musieć używać if'a. Tak jak powiedział @danek wyżej, jednymi z podstawowych funkcjami, których używasz w pracy z Monadami to flatMap - to właśnie ona zapewnia, że operacje są wykonywanie w sposób sekwencyjny (co wynika wprost z sygnatury operacji) oraz map. Jeśli musisz wyjąć wartość z kontekstu to zazwyczaj jest conajmniej kilka możliwości - różne 'orElse' czy pattern matching, w żadnym przypadku nie ma potrzeby używania ifów.

Używanie złych nazw metod nie ma żadnego związku z tym o czym mówiłem. Jeśli mam system typów który może działać na moją korzyść i wykrywać błędy na etapie kompilacji to bardzo chętnie będę taki mechanizm wykorzystywać. W przeciwieństwie do nazwy metod, których nie da się w żaden sposób zweryfikować.

0

Możesz też używać GOTO, też nic się nie stanie ;) Wyjątki są też bardziej zasobożerne. (zebranie całego stacktrace itp, zazwyczaj nie ma to znacznia, ale można to też mieć na uwadze). No i z try catchem wymuszasz też we wszystkim miejscach gdzie używasz dostosowanie do ewentualnych zmian.

Te ? z .neta nie wyglądają źle ale to tylko na map(). Jest więcej operacji na monadach ;)

0
DisQ napisał(a):
._. napisał(a):

Jeśli kierujesz się myślą im więcje ifów tym lepiej to pewnie masz rację - zwróć optionala.

A kto ci powiedział, że ta wartość coś zwraca?

To może używasz złych nazw metod...

Oczywiście nie kieruję się myślą, że im więcej ifów tym lepiej, ba, w przypadku Optional praktycznie nigdy nie powinieneś musieć używać if'a. Tak jak powiedział @danek wyżej, jednymi z podstawowych funkcjami, których używasz w pracy z Monadami to flatMap - to właśnie ona zapewnia, że operacje są wykonywanie w sposób sekwencyjny (co wynika wprost z sygnatury operacji) oraz map. Jeśli musisz wyjąć wartość z kontekstu to zazwyczaj jest conajmniej kilka możliwości - różne 'orElse' czy pattern matching, w żadnym przypadku nie ma potrzeby używania ifów.

Używanie złych nazw metod nie ma żadnego związku z tym o czym mówiłem. Jeśli mam system typów który może działać na moją korzyść i wykrywać błędy na etapie kompilacji to bardzo chętnie będę taki mechanizm wykorzystywać. W przeciwieństwie do nazwy metod, których nie da się w żaden sposób zweryfikować.

Ale ja cały czas używam takich monatów tylko w innej formie

return result.Where(x => x != null);
return firstValue?.SecoundValue ?? "something";
return result.DefaultIfEmpty(defaultObject);

Ja nie mogę zrozumieć dlaczego wy chcecie tego używać do łapanie wyjątków na zasadzie jakiegoś "promises"...?
Jeśli taki flatMap jest w gruncie anonimowy to jaką wnosi wartość dla osoby czytajęcej kod?

0

Nie chce używać tego do łapania wyjątków. Nie chce rzucać wyjątków w ogóle (chyba, że trzeba)

1

flatMap sam w sobie nie ma żadnego działania, to jest metoda o sygnaturze def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] i nic ponad to. O jej działaniu decyduje kontekst (F[_]). Jeśli chcesz mieć monadę do łapania wyjątków to używasz Try, jeśli do reprezentacji wartości opcjonalnych to Option, do DI Reader czy do zmiennego stanu State (więcej do poczytania chociażby tu: https://wiki.haskell.org/All_About_Monads).

0

No ale w dotnecie mogę sobie zwrócić default(int) to po co mi jakiś ekstra typ jak optional ?
Jak chce mieć readera to robie sobie klasę z generycznym typem i wewnątrz klasy tworzę instancje z tego typ'a po refleksji.
Zamiast nulowych ifów mogę użyć znaku zapytania.

To tak dla zasady mam używać, chain'nowych odpowiedników.?

No nie wiem czy to takie przydatne jest.

1 użytkowników online, w tym zalogowanych: 0, gości: 1