Kilka miesięcy temu zacząłem pracować nad nowym projektem, a kiedy przejrzałem kod, uderzyło mnie ilość użytych metod statycznych. collectionToCsvString(Collection<E> elements)
Przechowuje się w nich nie tylko metody użytkowe , ale także mnóstwo logiki biznesowej.
Kiedy zapytałem faceta odpowiedzialnego za uzasadnienie tego, powiedział, że to sposób na ucieczkę od tyranii Springa . Obejmuje to coś w tym procesie myślenia: w celu wdrożenia metody tworzenia paragonu klienta moglibyśmy mieć usługę
@Service
public class CustomerReceiptCreationService {
public CustomerReceipt createReceipt(Object... args) {
CustomerReceipt receipt = new CustomerReceipt();
// creation logic
return receipt;
}
}
Facet powiedział, że nie lubi niepotrzebnie zarządzać klasami przez Spring, głównie dlatego, że nakłada to ograniczenie, że klasy klientów muszą być same ziarenkami Spring. W końcu wszystko zarządza Spring, co zmusza nas do pracy z obiektami bezpaństwowymi w sposób proceduralny. Mniej więcej to, co podano tutaj https://www.javacodegeeks.com/2011/02/domain-driven-design-spring-aspectj.html
Więc zamiast powyższego kodu ma
public class CustomerReceiptCreator {
public static CustomerReceipt createReceipt(Object... args) {
CustomerReceipt receipt = new CustomerReceipt();
// creation logic
return receipt;
}
}
Mógłbym kłócić się do tego stopnia, że w miarę możliwości unikam Springowego zarządzania naszymi klasami, ale nie widzę korzyści z posiadania wszystkiego statycznego. Te metody statyczne są również bezpaństwowe, więc też nie bardzo OO. Czułbym się bardziej komfortowo z czymś takim
new CustomerReceiptCreator().createReceipt()
Twierdzi, że metody statyczne mają dodatkowe zalety. Mianowicie:
- Łatwiejszy do odczytania. Zaimportuj metodę statyczną i musimy tylko dbać o akcję, bez względu na to, która klasa to robi.
- Jest oczywiście metodą wolną od wywołań DB, więc pod względem wydajności jest tania; dobrze jest to wyjaśnić, aby potencjalny klient musiał wejść do kodu i to sprawdzić.
- Łatwiejsze pisanie testów.
Ale po prostu czuję, że jest w tym coś nie tak, więc chciałbym usłyszeć o tym bardziej doświadczone opinie programistów.
Moje pytanie brzmi: jakie są potencjalne pułapki tego sposobu programowania?
źródło
static
metoda jest zwykłą metodą fabryczną. Uczynienie metod fabrycznych statycznymi jest ogólnie przyjętą konwencją z wielu istotnych powodów. To, czy metoda fabryczna jest tutaj właściwa, to inna sprawa.Odpowiedzi:
Jaka jest różnica między
new CustomerReceiptCreator().createReceipt()
iCustomerReceiptCreator.createReceipt()
? Prawie żaden. Jedyną znaczącą różnicą jest to, że pierwszy przypadek ma znacznie bardziej niezręczną składnię. Jeśli zastosujesz się do pierwszego w przekonaniu, że unikanie metod statycznych sprawia, że Twój kod jest lepszy OO, poważnie się mylisz. Tworzenie obiektu tylko w celu wywołania na nim pojedynczej metody jest metodą statyczną według składni rozwartej.Sprawy wyglądają inaczej, gdy wstrzykujesz je
CustomerReceiptCreator
zamiastnew
je. Rozważmy przykład:Porównajmy to statyczna wersja metody:
Zaletą wersji statycznej jest to, że mogę łatwo powiedzieć, w jaki sposób współdziała ona z resztą systemu. Tak nie jest. Jeśli nie użyłem zmiennych globalnych, wiem, że reszta systemu nie została jakoś zmieniona. Wiem, że żadna inna część systemu nie może mieć wpływu na paragon. Jeśli użyłem niezmiennych obiektów, wiem, że kolejność się nie zmieniła, a createReceipt jest czystą funkcją. W takim przypadku mogę swobodnie przenosić / usuwać / etc to połączenie bez obawy o przypadkowe nieprzewidywalne efekty w innym miejscu.
Nie mogę dać takich samych gwarancji, jeśli wstrzyknąłem
CustomerReceiptCreator
. Może mieć stan wewnętrzny, który jest zmieniany przez połączenie, może mieć na mnie wpływ inny stan lub go zmienić. W mojej funkcji mogą występować nieprzewidywalne relacje między instrukcjami, tak że zmiana kolejności spowoduje zaskakujące błędy.Z drugiej strony, co się stanie, jeśli
CustomerReceiptCreator
nagle będzie potrzebować nowej zależności? Powiedzmy, że musi sprawdzić flagę funkcji. Gdybyśmy wstrzykiwali, moglibyśmy zrobić coś takiego:Następnie jesteśmy skończeni, ponieważ kod wywołujący zostanie wstrzyknięty za pomocą,
CustomerReceiptCreator
który zostanie automatycznie wstrzykniętyFeatureFlags
.Co jeśli użyjemy metody statycznej?
Ale poczekaj! kod telefoniczny również wymaga aktualizacji:
Oczywiście wciąż pozostaje pytanie, skąd się
processOrder
bierzeFeatureFlags
. Jeśli mamy szczęście, szlak kończy się tutaj, jeśli nie, konieczność przejścia przez FeatureFlags zostaje przesunięta w górę stosu.Tu jest kompromis. Metody statyczne wymagające jawnego przekazywania zależności, co powoduje więcej pracy. Metoda wstrzykiwana zmniejsza pracę, ale czyni domniemanie zależności, a tym samym ukrywa, czyniąc kod trudniejszym do uzasadnienia.
źródło