Kiedy stosować final

0

Zastanawiam się czy nie nadużywam final i zastanawia mnie Wasz pogląd na to.

Po 1, relacje typu service i repository czy jakakolwiek inna zależność, która sprawia że klasa A nie może działać bez klasy B, wtedy zawsze final się przydaje

public class SomeService {
    private final SomeRepository someRepository;
    
    public SomeService(SomeRepository someRepository) {
        this.someRepository = someRepository;
    }
}

Po 2, niemutowalne struktury danych, o ile tylko jest to możliwe i nie przeszkadza JPA czy cokolwiek innego

public class SomeDataClass {
    private final String data;

    public SomeDataClass(String data) {
        this.data = data;
    }
}

Po 3, zmienne w metodach

    public String search(String value) {
        final var searchResult = someRepository.find(value);
        return searchResult != null ? processResult(searchResult) : handleNoResult();
    }

Po 4, parametry metod

    public String search(final String value) { ... }

O ile pierwsze dwa przypadki jeszcze są dość często spotykane, to zauważyłem że w 3 i 4 jestem dość odosobniony przynajmniej tam gdzie pracuje. Jak to wygląda u Was? Dajecie final wszędzie bez zastanowienia, całkowicie tego unikacie, czy może zależy od sytuacji (jeżeli tak, to również kiedy stosujecie a kiedy nie)

Edit#

w sumie jest jeszcze 5 przypadek - final String method() i 6 - final class SomeClass { }. Osobiście chyba tego jeszcze nie użyłem w komercyjnym kodzie, ale również interesuje mnie Wasz pogląd na to

15

Kiedy stosować final ?

Zawsze. (Ja piszę tak jak podałeś w 1. 2. 3. i 4.)

Ihmo w Javie zrobiono błąd projektowy. W Scali jest trochę lepiej, ale dopierow Ruscie jest dobrze. Zmienne powinny być domyślnie finalne i dopiero po dodaniu słówka mut powinny stawaś się niefinalne

1

Pracowałem w projekcie, gdzie Eclipse był tak skonfigurowany, że przy "save" wstawiał "final" gdzie się da. Nie pamiętam problemów z tym.

1

Przy zmiennych i parametrach nie ma totalnie żadnego sens, zwłaszcza że optymalizator i tak je doda przy kompilacji gdzie się da.

Myślę że ideą większści ludzi było to, że final jest kolorowane jako słowo kluczowe i stanowi pewien wyznacznik deklaracji zmiennej, łatwiej go dostrzec. Zauważ że ludzie którzy piszą final przy zmiennych, jak przejdą na javę 11, to zaczynają używać var (też słowo kluczowe), mimo że jest to pewne przeciewieństwo, jedyną wspólną rzeczą jest że var to też słowo kluczowe, które IDE podświetla i też jest wyznacznikiem deklaracji.

Słówko wyjaśnienia - myślę że nie ma to żadnego sensu, ponieważ pracując lata w firmach w Javie, zauważyłem że final w żaden sposób nie "broni" przed przypadkowym przypisaniem, bo nawet jeśli ktoś przypisze drugi raz to nie zastanowi się "hmm, should this be final"? Tylko od razu uzna że tak i po prostu wymaże final z deklaracji.

final przy polach pisz zawsze kiedy przypisujesz zależności, które powinny być ustawione w konstruktorze (np. SomeRepository). Wyjątkiem są zależności dostarczane np przez springa i @Autowire, wtedy się nie da.

Kiedy masz obiekt stanowy no to siła rzeczy nie zrobisz żeby był immutable.

4

Klasy powinny być finał by default o ile nie są zaprojektowane na dziedziczenie, tak to jest w Kotlinie.

0
scibi92 napisał(a):

Klasy powinny być finał by default o ile nie są zaprojektowane na dziedziczenie, tak to jest w Kotlinie.

Mały problem. Sensowni programiści i tak nie używają dziedziczenia, i jedyne dla jakich to ma sens to abstract class. + Weź pod uwagę że wtedy ciężej byłoby pisać np framework do moków, albo robić anonmiowe klasy.

5

Weź pod uwagę że wtedy ciężej byłoby pisać np framework do moków

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

2

Klasy pod anonimowe klasy są na ogół zaprojektowane do dziedziczenia, a ograniczenie mocków na plus.
Poza tym dużo kodu infrastruktury jest dostępnych przez interfejsy a te można zamocować, sfejkowac itd

1

@KamilAdam: > ##### KamilAdam napisał(a):

Weź pod uwagę że wtedy ciężej byłoby pisać np framework do moków

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

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.

Np spróbuj kiedyś zrobić wzorzec projektowy, np Bridge albo Visitor, albo coś innego co wymaga wielu interfejsów, i potem te klasy podziel z Foo na FooProd implements Foo i FooTest implements Foo, żeby nie używać mocków ;D Good luck with that!

PS: Wiem, że kiedy pisze się końcowe aplikacje, które są do siebie podobne, nie mają wielu interfejsów i są bardzo warstwowe (controller, model, view) to może się to wydawać ok. Ale spróbuj kiedyś usiąśc nie nad aplikacją, tylko na bilbioteką, frameworkiem, albo innym rozwiązaniem które nie jest końcowe. Takie które nie jest warstwowe, które zależy od wielu rzeczy i które ma zmienne implementacje; takie w którym obsłużenie czegoś to nie jest zmapowanie modelu na widok, tylko zaimplementowanie całego algorytmu albo rozwiązania. Wtedy zauważysz że dodatkowa warstwa (+ interfejs + implementacja testowa) to jest bardzo duży koszt, dużo większy niż klasa otwarta do otestowania mockito. I gdybyś w takim projekcie, np w Springu, w Hibernacie powidział: "Hej ludzie, usuńmy mockito, zróbmy wszystkie klasy final, i wszystko co chcemy otestować to dodajmy do każdego miejsca interfejs i implementację testową", zgadnij co byś usłyszał. (i nie mówię tu o nakładzie pracy, tylko o tym że takie rozwiązanie zwiększyłoby NIEPORÓWNYWALNIE skomplikowanie takiej libki). Jeśli są już dwie implementacje i interfejs, to połowę pracy masz z głowy, ale jeśli nie mają i jest to jedna implementacja (bo ma być jedna) to cóż.

1

Aplikacje powinno testować się integracyjnie a nie testować mockito. A nawet jak już testować jednostkowo to mockowac infrastrukturę a nie inne klasy z logiką biznesowa. Frameworków nie rozwijałem to się nie wypowiem.

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