Czy użycie *** Helper lub *** Util klas zawierających tylko metody statyczne to AntiPattern

19

Często spotykam się z pomocnikami lub klasami Java w języku Java lub jakimkolwiek innym języku. Zadawałem więc sobie pytanie, czy jest to jakiś anty-wzór, a istnienie tego rodzaju klas to po prostu brak braków w projektowaniu i architekturze oprogramowania.

Często te klasy są ograniczone przy użyciu tylko metod statycznych, które robią wiele rzeczy. Ale przede wszystkim jest to zależne od kontekstu i stanowe.

Moje pytanie brzmi: co sądzisz o tego rodzaju statycznych klasach helper / util, ponieważ zaletą jest oczywiście szybkie wywoływanie przy użyciu samej nazwy klasy.

A na jakim poziomie abstrakcji uniknąłbyś korzystania z tego rodzaju klas?

Moim zdaniem słowo kluczowe „static” powinno być dozwolone w deklaracji klasy (Java), a nie w przypadku metod. Moim zdaniem użycie go w ten sposób może być dobrą alternatywą i środkiem do połączenia paradygmatów Procuedural i OO w Javie i uniknięcia niewłaściwego użycia słowa kluczowego.

Dodatki wynikające z odpowiedzi:

Na początku myślę, że łączenie różnych paradygmatów, a nawet używanie języków skryptowych interpretowanych w środowisku wykonawczym w kodzie skompilowanym maszynowo lub maszynowo jest całkowicie legalne.

Z mojego doświadczenia wynika, że ​​w trakcie procesu opracowywania projektu tego rodzaju pomocnicy i narzędzia, czy jak tam się nazywa, rosną i rosną i są wykorzystywane w każdym zapomnianym rogu bazy kodu, która pierwotnie została zaprojektowana jako modułowa i elastyczna. A ze względu na brak czasu na refaktoryzację lub ponowne przemyślenie projektu, z biegiem czasu pogorszysz sytuację.

Myślę, że staticpowinienem zostać usunięty z Javy. Zwłaszcza teraz, gdy możliwe jest użycie jeszcze bardziej wyrafinowanych funkcjonalnych elementów językowych.

Różnorodność
źródło
Myślę, że jeśli pomocnik nie musi tworzyć klasy, jest w porządku. Problem polega na tym, że pomocnik tworzy konkretną implementację klasy, a następnie nie można wyśmiewać tego interfejsu. Jeśli pomocnik przejdzie przez interfejs lub klasę abstrakcyjną, myślę, że jest w porządku.
bobek
W porządku, jeśli twierdzisz, że robisz programowanie proceduralne. Nie jest tak, jeśli twierdzisz, że programujesz obiektowo (zorientowany).
Zauważył
1
@Spotted: a ponieważ nie ma żadnej wartości biznesowej w twierdzeniu, że zajmujesz się wyłącznie programowaniem obiektowym, po prostu zawsze wykonuj mieszane paradygmaty i gotowe.
RemcoGerlich,
@RemcoGerlich Dokładnie. Chciałem zauważyć, że odpowiedź jest głównie „filozoficzna”, związana z tym, który paradygmat programowania bierzesz pod uwagę.
Zauważył

Odpowiedzi:

20

Cóż, Java nie ma wolnych funkcji, dlatego jesteś zmuszony umieścić je jako funkcje statyczne w jakiejś klasie pro-forma. Konieczne obejście nigdy nie jest anty-wzorem, choć może brakować elegancji.

Następnie z bezpłatnymi funkcjami nie ma nic złego. W rzeczywistości korzystanie z bezpłatnych funkcji zmniejsza sprzężenie, ponieważ mają one dostęp tylko do publicznego interfejsu zamiast do wszystkich krwawych szczegółów.

Oczywiście używanie funkcji swobodnych / statycznych w żaden sposób nie zmniejsza niebezpieczeństw związanych ze zmiennym stanem współdzielonym, szczególnie globalnym.

Deduplikator
źródło
2
@TimothyTruckle Albo są statyczne, albo mają zbędne thisi wymagają odpowiedniej instancji
Caleth
6
@TimothyTruckle Ponieważ to Java. Inne języki mogą mieć wolne funkcje bez konieczności umieszczania ich w klasie tylko po to, aby zadeklarować, że nie jest to metoda instancji. I w pewnym momencie musisz mieć ciasne połączenie. Czy to dobrze, czy nie, zależy wyłącznie od tego, co funkcja w pytaniu robi .
nvoigt
5
@TimothyTruckle Oczywiście, utrzymywanie stanu byłoby głównym powodem bycia „nie dobrym”.
nvoigt
2
@nvoigt, uważam, że odpowiedź ta zostałaby znacznie poprawiona poprzez wyraźne stwierdzenie tego. Stanowa statystyka jest zła; bezpaństwowcy są dobrzy. Naprawdę bardzo proste.
David Arno,
3
@TimothyTruckle To nie jest ciasne połączenie. Opisujesz luźne sprzężenie. Ścisłe sprzężenie w tym kontekście oznaczałoby pewną zależność między sobą. Zależność jednokierunkowa nie spełnia tego wymagania.
JimmyJames,
18

Statyczne funkcje narzędziowe lub pomocnicze nie są anty-wzorcem, jeśli są zgodne z pewnymi wytycznymi:

  1. Powinny być wolne od skutków ubocznych

  2. Służą do zachowania specyficznego dla aplikacji, które nie należy do klasy lub klas, w których działają te funkcje

  3. Nie wymagają żadnego wspólnego stanu

Typowe przypadki użycia:

  • Formatowanie dat w sposób specyficzny dla aplikacji
  • Funkcje, które przyjmują jeden typ jako dane wejściowe i zwracają inny typ.

Na przykład w aplikacji, nad którą pracowałem, użytkownicy przechowują wpisy do dziennika dotyczące ich działań. Mogą określić datę obserwacji i pobrać przypomnienie o wydarzeniu. Stworzyliśmy statyczną klasę narzędziową do pobierania pozycji dziennika i zwracania surowego tekstu dla pliku .ics.

Nie wymagało to żadnego wspólnego stanu. Nie mutuje żadnego stanu, a utworzenie zdarzenia iCal z pewnością było specyficzne dla aplikacji i nie należało do klasy pozycji dziennika.

Jeśli funkcje statyczne lub klasy użytkowe mają skutki uboczne lub wymagają stanu wspólnego, zaleciłbym ponowną ocenę tego kodu, ponieważ wprowadza on sprzężenie, które może być trudne do wyszydzenia w przypadku testów jednostkowych.

Greg Burghardt
źródło
4

Będzie to zależeć od twojego podejścia. Wiele aplikacji jest napisanych w sposób bardziej funkcjonalny, w przeciwieństwie do klasycznego sposobu myślenia (w tym większość witryn, na których się znajdujesz).

Przy takim sposobie myślenia na klasach pracowniczych będzie wiele metod narzędziowych, które można połączyć jako metody statyczne. Są one zasilane / używane bliżej zwykłych obiektów, które tylko przechowują dane i są przekazywane.

Jest to prawidłowe podejście i może działać bardzo dobrze, zwłaszcza na dużą skalę.

Tracker 1
źródło
„może działać bardzo dobrze, zwłaszcza na skali” Nie. Ciasne połączenie spowodowane statycznym dostępem wkrótce stanie na twojej drodze, gdy projekt będzie się rozwijał.
Timothy Truckle,
@TimothyTruckle Czy możesz wyjaśnić, w jaki sposób Agent.Save (obj) jest znacznie ściślej sprzężony niż obj.Save ()? Agent jest równie zdolny do uzyskania odwołania DI / IoC, nawet dla metod statycznych, jak instancja klasy. Dla porównania, sama Stack Exchange jest napisana w tego rodzaju nastawieniu i skalowała się lepiej niż jakakolwiek inna aplikacja ASP.Net z mniejszą ilością zasobów niż jakakolwiek inna, o której wiem.
Tracker1
„Czy możesz wyjaśnić, w jaki sposób Agent.Save (obj) jest znacznie ściślej sprzężony niż obj.Save ()?” To jest zły przykład. Prawidłowe Przykładem może być Agent.Save(obj)w porównaniu agentInstance.Save(obj). W tym drugim przypadku masz możliwość wstrzyknięcia do używanegoagentInstance kodu. W konsekwencji można wstrzykiwać w żadnej grupie dzieci o Agentzbyt która umożliwia ponowne wykorzystanie og użyciu kodu z różnych „typów” o Agentbez rekompilacji. To jest niskie sprzężenie .
Timothy Truckle,
Myślę, że tak naprawdę sprowadza się to do kilku rzeczy. Czy stan jest potrzebny, jak elastyczny musi być i czy dobrze jest mieć stan globalny / instancja? Decyzja należy do każdego dewelopera / architekta. Wolę prostszą bazę kodu niż zwiększanie złożoności scenariuszy, które sprawiają, że wszystko jest trudniejsze do zrozumienia jako całości. Jedną z rzeczy, które uwielbiam w node / js, jest to, że mogę pisać prosty / czysty kod i nadal wstrzykiwać do testowania bez kodu zapisanego w specyfikacjach DI / IoC.
Tracker 1
2

Osobiście uważam, że jedyną ważną częścią takich klas pomocniczych jest to, aby stały się prywatne. Poza tym - są ściśle dobrym pomysłem (stosowane oszczędnie).

Myślę o tym - jeśli we wdrażaniu klasy (lub funkcji) - coś takiego jest pomocne i sprawia, że ​​implementacja jest jaśniejsza, jak może być źle? I CZĘSTO bardzo ważne jest zdefiniowanie takich prywatnych klas pomocników, aby umożliwić integrację z innymi algorytmami i korzystanie z nich, zależnie od tego, czy dane mają określony kształt.

To, czy nazwać go „pomocnikiem”, jest drobną kwestią osobistego gustu, ale oznacza, że ​​pomaga we wdrażaniu i nie jest interesujące dla szerszej publiczności. Jeśli to ma sens - idź!

Lewis Pringle
źródło
1
Klasy użyteczności publicznej mogą być nadal pomocne. Najlepszym przykładem: java.lang.Math.
amon
1

Występowanie staticklas pomocniczych opiera się na nieporozumieniu. To, że nazywamy klasy tylko staticmetodami „klasami użyteczności”, nie oznacza, że ​​nie można pisać typowych zachowań w POJO.

static klasy pomocnicze są anty-wzorcowe z trzech powodów:

  1. Statyczny dostęp do metod pomocniczych ukrywa zależności . Gdyby te „klasy użytkowe” były POJO, można je wprowadzić do klasy zależnej jako parametry konstruktora, co uczyniłoby zależność oczywistą dla każdego użytkownika klasy zależnej.

  2. Statyczny dostęp do tych metod pomocniczych powoduje ścisłe połączenie . Oznacza to, że kod korzystający z metod pomocniczych jest trudny do ponownego użycia i (jako efekt uboczny) testowany.

  3. Zwłaszcza jeśli utrzymują stan, są to jedynie zmienne globalne . I mam nadzieję, że nikt nie twierdzi, że zmienne globalne są jakieś dobre ...

Statyczne klasy pomocnicze są częścią anty-wzorca kodu STUPID .


Stan globalny nie ma związku z pytaniem, - max630

OP napisał:

Ale przede wszystkim jest to zależne od kontekstu i stanowe.

Statyczne konstrukty stanowe to stany globalne.

dowolny kod może z niego korzystać. - max630

Tak, z powodu. I prawie wszystkie aplikacje wymagają pewnego rodzaju stanu globalnego .

Ale stan globalny ! = Zmienna globalna .

Możesz stworzyć stan globalny za pomocą technik OO poprzez wstrzykiwanie zależności, ale nie można uniknąć stanu globalnego za pomocą stanowych struktur statycznych, które są zmiennymi globalnymi na dole.

Timothy Truckle
źródło
2
Stan globalny nie ma związku z pytaniem, każdy kod może go użyć.
max630
@ max630 proszę zobaczyć moją aktualizację
Timothy Truckle,
1
Możesz używać funkcji bezstatycznych bez sprzężenia. Mijasz Func<Result, Args>obiekty, które są tworzone w innym miejscu.
Caleth
1
1) Ogólnie myślę, że ukrywanie implementacji jest dobrą rzeczą, jeśli jest odpowiednie. 2) Posiadanie wszystkiego jako argumentu / zależności wprowadza również rodzaj sprzężenia w czasie projektowania, możesz poczuć to, gdy musisz spędzić dużo czasu na zmianie podpisów w długich łańcuchach zależności. I tak jak każda funkcja wymagająca Loggera lub inne przekrojowe problemy jako argument, pilne ... (Ale tak, może to utrudnić testowanie na niskim poziomie) 3) Oczywiście funkcje statyczne powinny być bezstanowe.
Alex
1
@Alex Pozwólcie, że powiem to na odwrót: jednostka to dowolna wiązka kodu (w tym statyczne odniesienia do „narzędzi”), które mają ten sam powód do zmiany . Wszystko w tym urządzeniu jest szczegółem implementacji . Wszystko inne jest zależnością , a jednostka nie powinna mieć do niej statycznego dostępu.
Timothy Truckle,