Podobno metody statyczne to zło: https://4programmers.net/Forum/Kosz/365170-zadanie_java_packages?page=1 (link się pewnie zaczerwieni niedługo, ale cóż)
Czemu?
Pod warunkiem, że funkcja statyczna jest czysta: Czym się różni metoda statyczna od funkcji znanej z funkcyjnych języków programowania? W zasadzie niczym i to pozwala nam na sprowadzenie takiego twierdzenia do absurdu, bo sprowadza się ono do zakazu korzystania z paradygmatu funkcyjnego.
Przynajmniej w C# Jeśli mamy funkcję niestatyczną, która nie korzysta z żadnych pól / properties / itp swojej klasy ani nie stanowi implementacji interfejsu to kompilator radzi, by zmienić ją na statyczną. I słusznie, bo jej nie-statyczność nic nie zdaje się wnosić.
Kilka błędów tu widzę - po pierwsze, pewnie @KamilAdam się wypowie, ale to nie jest prawda że w paradygmacie funkcyjnym da się pisać tylko funkcjami. Metodami w obiektach też się da, to po pierwsze.
Po drugie, owszem - w moim poście z tamtego tematu zasugerowałem, że metody statyczne właściwie niczym się nie różnią od funkcji globalnych, i nadal to podtrzymuję. W niektórych językach, takich jak Python, PHP czy JavaScript, można deklarować funkcje jako tzw. first-class citizen (czyli możemy mieć funkcje, a nie metodę czy metodę statyczną). I tak jak o ile w takim JS'ie, Pythonie i PHP można rozumieć projekt który jest pisany takim stylem, tak w Javie i C# (przynajmniej dla mnie), to całkowicie nie ma sensu. Np zadanie z tego wątku: https://4programmers.net/Forum/Kosz/365170-zadanie_java_packages?page=1 całkowicie nie pasuje do funkcji statycznych, i jest to doskonały przykład jak nie dobierać rozwiązania do problemu.
Oczywiście, nie wszystkie metody statyczne mają tą wadę. Jak pisane było wcześniej, metody które pełnią funkcję "nazwanego konstruktora", jak Optional.of()
albo Array.stream()
są jak najbardziej okej. Możnaby je zamienić na konstruktor z przeładowanymi argumentami, ale takie "nazwane" instancjonowanie jest czytelniejsze. Dodatkowo, jest kilka ograniczonych metod, które mogłyby być funkcjami globalnymi, jak np Math.min()
, Math.max()
, Math.abs()
, i one również są okej jako metody statyczne. Pewnie znalazłyby się kilka takich przypadków, może jak String.join()
, ale to są raczej ograniczone przypadki. Wtedy taka klasa pełni trochę rolę namespace'a. Ciężko znaleźć koncepcyjną różnicę między metodą statyczną Math.max()
, a funkcją globalną max()
w namespace'ie Math
. Można w Javie nawet dodać import static
i używać normalnie return max(2,3);
tak jakby faktycznie była funkcją globalną.
Problem się zaczyna, kiedy zaczynamy umieszczać nietrywialną logikę w funkcjach statycznych, albo robimy wszelakiego rodzaju "helpery" czy "utilsy" które nie są niczym innym jak tylko funkcjami globalnymi które łamią enkapsulację, i są wsadzone jako takie static
methody do jakiejś klasy do której nie należą. Moim zdaniem wynika to z kilku błędów programistów, a najbardziej z tego, że niektórzy po prostu nie umieją dobrze projektować swoich klas, nazywać klas, dbać o SRP, odpowiednio organizować klas oraz znajdować odpowiednich proporcji i rozgraniczenia pomiędzy "domain-classes" a "general-purpose classes" (często niepotrzebnie przechylonych w stronę tego drugiego), i z tej nieumiejętności właśnie rodzą się statyczne metody - nieokrzesane bloby kodu niewiadomego pochodzenia który nie wiadomo gdzie pasuje, i nie wiadomo na czym operuje.
Dodatkowo, jak metoda jest niestatyczna, to łatwiej jest napisać kod, który jest cohesive, czyli "bliskie rzeczy są blisko, dalekie są daleko". Jak mamy publiczną metodę statyczną, to czasem aż prosi się żeby użyć jej tam gdzie się nie powinno. Np metody FileSystem.splitLines()
, podczas wrapowania HTML'a w HtmlParser
. No bo skoro jest gdzieś metoda statyczna do splitowania, to czemu jej nie użyć - a dobra praktyka (żeby nie łączyć systemu plików z HTMLem) jest olana. Z tego powodu metody statyczne łatwo jest użyć raz tu, raz tu, raz tu - często w jednej klasie mamy różne metody statyczne używane w czasem różnych modułach, zwłaszcza jak taka klasa jest typowym "utilsem", czyli nigdy nie jest instancjonowana, i ma same static
i, i nie dość że w ten sposób robimy duże spaghetti, to robimy ukryte powiązania takim static
iem między klasami.