Kiedy stosować final

Kiedy stosować final
jarekr000000
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: U krasnoludów - pod górą
  • Postów: 4712
3

Wrzucam praktycznie wszędzie.
Robię final klasy. (Więc na metody nie muszę).
Męczące to, ale java ma dużo bardziej męczące miejsca.
(więc już raczej mało w javie piszę).

Interfejsów nie robię za często.
Mockito też nie stosuję prawie nigdy.

Noi kiedyś dokładnie tak robiono, interfejs, i dwie implementacje, jedna testowa, jedna produkcyjna. I zgadnij co. To było dużo niepotrzebnej pracy, bo zamiast dodatkowego interfejsu i klasy, można to opendzlować jednym when() z mockito. To są tylko testy. Poza tym czasem kod spodziewa się konkretnej implementacji jednego interfejsu - i co, potem zrobisz interfejs dziedziczący z interfejsu, żeby drugą implementacje otestować? 2/10.

Z ciekawostek - w jednej firmie w końcu dostaliszmy szału od Mockito, bo spokojnie można było pół kodu wywalić a testy były zielone. Co akurat jest spoko, tylko że tu system nie działał użytkownikom po tej operacji. Po wywaleniu mockito i przejściu na ręcznie robione testowe implementacje... ilość linii kodu w testach drastycznie spadła.
Jeśli to kogoś dziwi: mockito robione do przesady prowadzi szybko do setek powtarzających się sekwencji mockito.when - można przeoczyć, że to cały czas te prawie te same linijki.

S9
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Warszawa
  • Postów: 3573
0

@wartek01: No to jak pisałem, jak kod to sama infrastruktura to ta architektura raczej zbyt dużo sensu nie ma.

FI
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 470
0

najczesciej tylko pola w klasie zeby pokazac co jest 5, bawienie sie w finalowanie wszystkieo i wszedzie to sztuka dla sztuki

somekind
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Wrocław
3
KamilAdam napisał(a):

I dobrze. To co musi być mokowane powinno mieć wydzielony interfejs.

Nie wierzę, co czytam. Zazwyczaj Javowcy na tym forum chełpią się tym, że w Javie nie trzeba żadnych interfejsów, aby móc wszystko zawsze mockować, a tu taka miła niespodzianka.

TomRiddle napisał(a):

Tak, ale w sytuacji w której nie ma takich planów, i masz tylko jedną implementację, to to nie ma sensu. Dlatego że:
a) Jeśli masz jedną implementacje, to zawsze używasz tej jednej i ten interfejs nie daje żadnej korzyści

Korzyść jest mniej więcej taka sama jak z nietrzymania jedzenia i odzieży w kociej kuwecie.

scibi92 napisał(a):

Tak, bo interface to kontrakt.

Tak tylko uściślę - interfejsy czasami są kontraktami. A czasami nawet abstrakcjami. Interfejs sam w sobie to tylko konstrukcja języka.

TomRiddle napisał(a):

Poza tym nadal nie odniosłeś się do mojego poprzedniego pytania, co w styacji kiedy logika jest bardzo złożona i granica pomiędzy (tym co nazwałeś) "infrastrukturą" i tym co nazwałeś "domeną" się zaciera. Bo oczywiście, kiedy piszesz apkę dla usera podpartą libkami i frameworkami to logiki masz bardzo mało, i łatwo sobie powiedzieć: "ta klasa to domena, ta klasa to infrastruktura". Ale kiedy tych klas jest dużo więcej, jak np w jakiejś konkretnej branżowej skomplikowanej logice, to rozróżnienie się zaciera.

No jeśli to rozróżnienie się "zaciera" to znaczy, że powstaje kod spaghetti. Bo nie da się trzymać SRP i mieć zatartej granicy między domeną, a infrastrukturą.

Ale zaprojektuj np edytor tekstu (podobny do worda), albo painta, odtwarzacz wideo, grę w szachy online, program wizualizujący fractale, narysuj drzewo w opengl'u, program konwertujący svg na png, klienta http jak curl, nakładkę na osa która wyświetla ikonki przy folderach

W wordzie domeną jest treść i operacje na niej, infrastrukturą jest zapis do pliku/drukarki oraz GUI dla użytkownika. Analogicznie jest w paincie. W szachach też masz jasny podział między logiką: stanem planszy, historią ruchów ich walidacją, AI, a GUI dla użytkownika oraz komunikacją sieciową. Konwersja formatów plików tak samo - algorytm konwersji jest logiką, infrastrukturą jest zapis i odczyt do plików.

  • w skrócie jaką kolwiek aplikacje w której logiki jest dużo, a warstw jest mało. I wtedy wypowiedz się, ile razy w takich aplikacjach pojawił się problem oddzielenia domeny od infrastruktury.

Warstw zawsze powinno być tyle samo (3). A im więcej logiki, tym większy sens ma jej oddzielanie od infrastruktury.

Charles_Ray
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 1910
1

IMHO są 3 etapy:

  1. Nie stosujesz final, mutujesz wszystko dookoła, nie ogarniasz kuwety.
  2. Odkrywasz final i stosujesz go wszędzie, bo przecież w Scali jest „val”. Twój kod nie zawiera głupich błędów, aczkolwiek nie ma to żadnego wpływu na jego „czystość”, ani na „jakość”. Jest lepiej niż bez final, chociaż wszędzie jest naćkane finalami.
  3. Nie stosujesz final, ponieważ przestrzegasz konwencji i higieny pracy. Rozumiesz (Ty i twój zespół), że fakt iż możesz coś mutować/rozszerzyć nie oznacza, że powinieneś. Podejmujesz świadome decyzje. Przerzucasz ciężar na konwencje, code review oraz testy. Raczej nie do zastosowania w systemie legacy, w którym koduje 10 pokolenie.
jarekr000000
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: U krasnoludów - pod górą
  • Postów: 4712
3

Przerzucasz ciężar na konwencje, code review oraz testy.

To coś mocno poszło nie tak. Po to mamy kompilatory żeby mniej było do dumania na code review i mniej durnych testów.

IMO punkt 3 to utopia z innego powodu: jak już mam na tyle sensowny zespół, że nie muszę się o final martwić ... to przeważnie nie piszemy w javie.

99xmarcin
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 2420
1

Jak ja lubię i robię:

  • final na polach klasy wszędzie gdzie się da. Robienie immutable klas bardzo pomaga żeby dodawać final wszędzie.
  • Ponieważ dziedziczenia unikam jak ognia, to final ani na metodach ani na klasach nie stosuje. Założenie jest takie że zespół rozumie że dziedziczenie jest słabe i nie trzeba ich final'ami odstraszać.
  • Nigdy nie stosuje final wewnątrz metod. Dlaczego: 1) Zaciemnia to kod, final jako słowo kluczowe jest wygubione - zamiast nazw zmiennych wiedzę falę powtarzających się wygrubionych słów 2) Metody powinna być w miarę krótka i już na pierwszy rzut oka powinno być widać co się zmienia a co nie.

W C# w którym sporo pisałem, większość programistów używa var do deklaracji zmienny. var w C# podobnie jak w ostatnich wersjach Javy jest mutowalny. Przez te ponad 5+ lat przygód z C# jeszcze nigdy nie spotkałem się z sytuacją że ktoś zmutował przez przypadek zmienną lokalną, która powinna być niemutowalna. To co najczęściej się zdarzało to mutowanie obiektów w pamięci które mutowane nie powinny być, ale przed tym i final nie chroni:

Kopiuj
final String[] args  = new String[] { "foo" };
args[0] = "bar";

W Scali jest val (niemutowalne) i var. Prawie wszędzie używam val dlatego że, w przeciwieństwie do final użycie nic nie kosztuje (ilość wpisanych znaków), i czytelność nie ponosi na tym szkody.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.