Obecnie pracuję nad większym projektem solo i mam kilka klas, w których nie widzę powodu, aby tworzyć instancję.
Na przykład moja klasa kości przechowuje teraz statycznie wszystkie dane, a wszystkie metody są również statyczne. Nie muszę go inicjować, ponieważ kiedy chcę rzucić kostką i uzyskać nową wartość, po prostu używam Dice.roll()
.
Mam kilka podobnych klas, które mają tylko jedną taką główną funkcję i zamierzam rozpocząć pracę nad rodzajem „kontrolera”, który będzie odpowiedzialny za wszystkie zdarzenia (np. Kiedy gracz się poruszy i jaka tura to jest) i odkryłem, że mógłbym zastosować ten sam pomysł w tej klasie. Nigdy nie planuję tworzenia wielu obiektów dla tych konkretnych klas, więc czy złym pomysłem byłoby uczynienie ich w pełni statycznymi?
Zastanawiałem się, czy jest to uważane za „złą praktykę”, jeśli chodzi o Javę. Z tego, co widziałem, społeczność wydaje się być podzielona na ten temat? W każdym razie chciałbym trochę o tym dyskutować, a linki do zasobów też byłyby świetne!
Odpowiedzi:
Nie ma nic złego w klasach statycznych, które są naprawdę statyczne . Innymi słowy, nie istnieje żaden stan wewnętrzny, który mógłby zmienić dane wyjściowe metod.
Jeśli
Dice.roll()
po prostu zwraca nowy losowy numer od 1 do 6, to nie zmienia stanu. To prawda, że współdzieliszRandom
instancję, ale nie uważam, że zmiana stanu z definicji zawsze będzie dobra, losowa. Jest również bezpieczny dla wątków, więc nie ma tu żadnych problemów.Często zobaczysz ostatecznego „Pomocnika” lub inne klasy narzędzi, które mają prywatnego konstruktora i członków statycznych. Prywatny konstruktor nie zawiera logiki i służy jedynie do zapobiegania tworzeniu instancji klasy przez kogoś. Ostatni modyfikator sprowadza ten pomysł do domu, że nie jest to klasa, z której nigdy nie chciałbyś czerpać. To tylko klasa użyteczności. Jeśli zostanie to wykonane poprawnie, nie powinno być singletonów ani innych członków klasy, którzy sami nie są statyczni i ostateczni.
Dopóki postępujesz zgodnie z tymi wytycznymi i nie tworzysz singli, nie ma w tym absolutnie nic złego. Wspominasz o klasie kontrolera, a to prawie na pewno będzie wymagało zmian stanu, więc odradzam stosowanie tylko metod statycznych. Możesz w dużej mierze polegać na statycznej klasie narzędzi, ale nie możesz uczynić go statyczną klasą narzędzi.
Co uważa się za zmianę stanu klasy? Cóż, wykluczmy liczby losowe na sekundę, ponieważ z definicji nie są deterministyczne, a zatem wartość zwracana często się zmienia.
Czysta funkcja to taka, która jest deterministyczna, co oznacza, że dla danego wejścia otrzymasz jeden i dokładnie jeden wynik. Chcesz, aby metody statyczne były czystymi funkcjami. W Javie istnieją sposoby poprawiania zachowania metod statycznych w celu utrzymania stanu, ale prawie nigdy nie są to dobre pomysły. Kiedy zadeklarujesz metodę jako statyczną , typowy programista natychmiast przyjmie, że jest to czysta funkcja. Odbiegając od oczekiwanego zachowania, ogólnie rzecz biorąc, masz tendencję do tworzenia błędów w swoim programie i należy tego unikać.
Singleton jest klasą zawierającą metody statyczne, które są jak najbardziej przeciwne „czystej funkcji”. Pojedynczy statyczny element prywatny jest przechowywany wewnętrznie w klasie, która służy do zapewnienia, że istnieje dokładnie jedna instancja. To nie jest najlepsza praktyka i może sprawić kłopoty później z wielu powodów. Aby dowiedzieć się, o czym mówimy, oto prosty przykład singletona:
źródło
Dice.roll()
nie jest prawidłowym wyjątkiem od reguły „brak stanu globalnego”.random.nextInt(6) + 1
. ;)RollAndMove()
Rzuci się ponownie, gdy dostarczymy podwójną szóstkę, to najłatwiejszym, najsolidniejszym sposobem, aby to zrobić, jest wykpienie jednego z nichDice
lub generatora liczb losowych. Ergo,Dice
nie chce być klasą statyczną, używając statycznego generatora losowego.Aby dać przykład ograniczeń
static
klasy, co jeśli niektórzy z twoich graczy chcą uzyskać niewielką premię za rzuty kostkami? I są gotowi zapłacić duże pieniądze! :-)Tak, można dodać inny parametr, tak
Dice.roll(bonus)
,Później potrzebujesz D20.
Dice.roll(bonus, sides)
Tak, ale niektórzy gracze mają atut „wyjątkowo sprawny”, więc nigdy nie mogą „zmarnować” (rzucić 1).
Dice.roll(bonus, sides, isFumbleAllowed)
.Robi się bałagan, prawda?
źródło
new Dice().roll(...)
musi być concidered dostępu statycznego , jak równieżDice.roll(...)
. Korzystamy tylko wtedy, gdy wstrzykniemy tę zależność.static
bałagan zdarza się przy każdym połączeniu, a potrzebna wiedza jest wymagana przy każdym połączeniu, więc globalnie rozpryskuje się po całej aplikacji. W strukturze OOP tylko konstruktor / fabryka musi być niechlujny, a szczegóły tej matrycy są zamknięte. Następnie użyj polimorfizmu i po prostu zadzwońroll()
. Mogę edytować tę odpowiedź, aby wyjaśnić.W szczególnym przypadku klasy kości myślę, że użycie metod instancji zamiast statyki znacznie ułatwi testowanie.
Jeśli chcesz przetestować na jednostce coś, co wykorzystuje instancję kości (np. Klasę gry), to z twoich testów możesz wstrzyknąć formę testu podwójnej kości, która zawsze zwraca ustaloną sekwencję wartości. Twój test może sprawdzić, czy gra ma odpowiedni wynik dla tych rzutów kostką.
Nie jestem programistą Java, ale myślę, że byłoby to znacznie trudniejsze w przypadku całkowicie statycznej klasy Dice. Zobacz /programming/4482315/why-does-mockito-not-mock-static-methods
źródło
Jest to tak naprawdę znane jako wzorzec Monostate , w którym każda instancja (a nawet „brak instancji”) ma ten sam status. Każdy członek jest członkiem klasy (tzn. Nie ma członków instancji). Są one powszechnie stosowane do implementowania klas „zestawu narzędzi”, które zawierają zestaw metod i stałych związanych z pojedynczą odpowiedzialnością lub wymaganiem, ale nie potrzebują stanu do działania (są one czysto funkcjonalne). W rzeczywistości Java jest dostarczana z niektórymi z nich w pakiecie (na przykład matematyka ).
Trochę nie na temat: rzadko zgadzam się z nazwami słów kluczowych w VisualBasic, ale w tym przypadku myślę, że
shared
jest z pewnością jaśniejszy i semantycznie lepszy (jest dzielony między samą klasą i wszystkimi jej instancjami) niżstatic
(pozostaje po cyklu życia zakresu, w którym jest zadeklarowany).źródło