Dlaczego tworzenie generycznego setera i gettera z refleksją jest złym pomysłem?

49

Jakiś czas temu napisałem tę odpowiedź na pytanie, w jaki sposób uniknąć pobierania i ustawiania każdej zmiennej zmiennej. W tym czasie miałem tylko trudne do zwerbowania przeczucie, że to zły pomysł, ale OP wyraźnie pytał, jak to zrobić. Szukałem tutaj, dlaczego może to stanowić problem, i znalazłem to pytanie , na które odpowiedzi wydaje się przyjmować, że używanie refleksji, chyba że absolutnie konieczne, jest złą praktyką.

Czy zatem ktoś może werbalizować, dlaczego należy unikać refleksji, szczególnie w przypadku generycznego gettera i setera?

HAEM
źródło
12
Jeśli chcesz po prostu zredukować płytę kotła, zarówno Lombok, jak i Groovy będą generować gettery i setery cicho, ale bezpiecznie.
Chrylis -on strike-
2
Jak w ogóle użyłbyś tych pobierających i ustawiających w statycznym języku, takim jak Java? Wydaje mi się, że nie zaczynasz.
Esben Skov Pedersen
@EsbenSkovPedersen Można by spożywać je podobnie jak Map„s getoraz put, co jest dość niezgrabne sposób robienia rzeczy.
HAEM,
2
IIRC, Lombok syntetyzuje metody pobierające / ustawiające w czasie kompilacji, zgodnie z odpowiednią adnotacją, więc: nie ponoszą kary za odbicie, mogą być analizowane / wstawiane, mogą być refaktoryzowane (IntelliJ ma wtyczkę do Lombok)
Alexander,

Odpowiedzi:

117

Wady refleksji w ogóle

Odbicie jest trudniejsze do zrozumienia niż kod liniowy.

Z mojego doświadczenia wynika, że ​​refleksja jest funkcją Java na poziomie „eksperta”. Twierdziłbym, że większość programistów nigdy nie używa aktywnie odbicia (tzn. Zużycie bibliotek korzystających z odbicia się nie liczy). Utrudnia to zrozumienie kodu dla tych programistów.

Kod odbicia jest niedostępny dla analizy statycznej

Załóżmy, że mam getFooklasę pobierającą i chcę zmienić jej nazwę na getBar. Jeśli nie użyję refleksji, mogę po prostu przeszukać bazę kodu getFooi znajdę każde miejsce, które korzysta z gettera, abym mógł go zaktualizować, a nawet jeśli go przegapię, kompilator narzeka.

Ale jeśli miejsce, w którym używa się gettera, jest podobne callGetter("Foo")i callGetterdziała getClass().getMethod("get"+name).invoke(this), to powyższa metoda go nie znajdzie, a kompilator nie będzie narzekał. Tylko wtedy, gdy kod zostanie faktycznie wykonany, otrzymasz NoSuchMethodException. Wyobraź sobie ból, który zostanie przełknięty, jeśli ten wyjątek (który jest śledzony) zostanie połknięty, callGetterponieważ „jest używany tylko z zakodowanymi ciągami, tak naprawdę nie może się zdarzyć”. (Nikt by tego nie zrobił, ktoś mógłby się spierać? Tyle że OP zrobił dokładnie to samo w swojej odpowiedzi SO. Jeśli nazwa pola zostanie zmieniona, użytkownicy standardowego setera nigdy nie zauważą, z wyjątkiem wyjątkowo niejasnego błędu setera, który po cichu nic nie robi. Użytkownicy gettera mogą, jeśli mają szczęście, zauważyć wyjście konsoli zignorowanego wyjątku).

Kod odbicia nie jest sprawdzany przez kompilator

Jest to zasadniczo duży podpunkt powyższego. Chodzi o kod refleksyjny Object. Typy są sprawdzane w czasie wykonywania. Błędy są wykrywane podczas testów jednostkowych, ale tylko w przypadku posiadania zasięgu. („To tylko getter, nie muszę go testować.”) Zasadniczo, tracisz przewagę, używając Java nad Pythonem, zyskałeś przede wszystkim.

Kod odbicia jest niedostępny do optymalizacji

Może nie w teorii, ale w praktyce nie znajdziesz JVM, która wstawia lub tworzy wbudowaną pamięć podręczną Method.invoke. Do takich optymalizacji dostępne są normalne wywołania metod. To sprawia, że ​​są znacznie szybsze.

Kod refleksji jest po prostu powolny

Dynamiczne wyszukiwanie metod i sprawdzanie typu niezbędne do kodu odbicia jest wolniejsze niż normalne wywołania metod. Jeśli zamienisz ten tani geter jednowierszowy w bestię odbicia, możesz (nie zmierzyłem tego) patrzeć na kilka rzędów wielkości spowolnienia.

Wadą specyficznego gettera / setera

To po prostu zły pomysł, ponieważ twoja klasa nie ma już enkapsulacji. Każde pole, które ma, jest dostępne. Równie dobrze możesz je wszystkie upublicznić.

Sebastian Redl
źródło
8
Trafiłeś wszystkie najważniejsze punkty. Właściwie idealna odpowiedź. Mógłbym się spierać, że osoby pobierające i ustawiające są nieznacznie lepsze niż pola publiczne, ale większy punkt jest poprawny.
JimmyJames,
2
Warto również wspomnieć, że gettery / settery mogą być łatwo generowane również przez IDE.
stiemannkj1
26
+1 Logikę debugowania debugowania w projekcie wielkości produkcji należy uznać za torturę przez ONZ i nielegalnie.
errantlinguist,
4
„trudniejsze do zrozumienia dla tych programistów”. - dla tych programistów jest to znacznie trudniejsze do zrozumienia, ale trudniejsze do zrozumienia dla wszystkich, bez względu na poziom umiejętności.
BartoszKP
4
„Kod odbicia jest niedostępny dla analizy statycznej” - To. Jest to bardzo ważne dla umożliwienia refaktoryzacji. W miarę, jak narzędzia analizy statycznej stają się mocniejsze (np. Wyszukiwanie kodu w najnowszym serwerze Team Foundation Server), pisanie kodu, który je umożliwia, staje się jeszcze bardziej wartościowe.
Dan J
11

Ponieważ getter / setery tranzytowe są obrzydliwością, która nie daje żadnej wartości. Nie zapewniają żadnej enkapsulacji, ponieważ użytkownicy mogą uzyskać rzeczywiste wartości za pośrednictwem właściwości. Są to YAGNI, ponieważ rzadko, jeśli w ogóle, zmienisz implementację pamięci w terenie.

A ponieważ jest to mniej wydajne. Przynajmniej w języku C # getter / setter wstawi się bezpośrednio do pojedynczej instrukcji CIL. W odpowiedzi zastępujesz to 3 wywołaniami funkcji, które z kolei muszą załadować metadane, aby się nad nimi zastanowić. Zasadniczo używałem 10x jako ogólnej zasady kosztów refleksji, ale kiedy szukałem danych, jedna z pierwszych odpowiedzi zawierała tę odpowiedź, która zbliżyła ją do 1000x.

(Utrudnia to debugowanie kodu i szkodzi użyteczności, ponieważ istnieje więcej sposobów jego niewłaściwego użycia, a także szkodzi konserwacji, ponieważ teraz używasz magicznych ciągów zamiast odpowiednich identyfikatorów ...)

Telastyn
źródło
2
Zauważ, że pytanie jest oznaczone jako Java, która ma tak zachwycające badziewie jak koncepcja Beans. Fasola nie ma pól publicznych, ale może ujawniać pola getX()i setX()metody, które można znaleźć poprzez odbicie. Fasola niekoniecznie musi zawierać w sobie swoje wartości, mogą być po prostu DTO. Tak więc w Javie narzędzia pobierające są często niezbędnym bólem. Właściwości C # są znacznie, o wiele ładniejsze pod każdym względem.
amon
11
Jednak właściwości C # to przebrane gettery / setters. Ale tak, koncepcja Fasoli to katastrofa.
Sebastian Redl
6
@amon Racja, C # wpadł na straszny pomysł i jest łatwiejszy do wdrożenia.
JimmyJames
5
@JimmyJames To naprawdę nie jest straszny pomysł; możesz zachować zgodność ABI pomimo zmian w kodzie. Przypuszczam, że wielu nie ma problemu.
Casey,
3
@Casey Zakres tego wykracza poza komentarz, ale tworzenie interfejsu z wewnętrznych szczegółów implementacji jest odwrotne. Prowadzi to do opracowania procedury. Są chwile, kiedy posiadanie metody getX jest właściwą odpowiedzią, ale jest dość rzadkie w dobrze zaprojektowanym kodzie. Problem z modułami pobierającymi i ustawiającymi w Javie nie polega na tym, że otaczają je kody, ale są częścią okropnego podejścia projektowego. Ułatwienie to jak uderzenie się w twarz.
JimmyJames,
8

Oprócz przedstawionych już argumentów.

Nie zapewnia oczekiwanego interfejsu.

Użytkownicy oczekują wywoływania foo.getBar () i foo.setBar (wartość), a nie foo.get („bar”) i foo.set („bar”, wartość)

Aby zapewnić interfejs oczekiwany przez użytkowników, należy napisać osobny moduł pobierający / ustawiający dla każdej właściwości. Gdy już to zrobisz, nie ma sensu używać odbicia.

Peter Green
źródło
Wydaje mi się, że ten powód jest ważniejszy niż wszystkie inne w innych odpowiedziach.
Esben Skov Pedersen,
-4

Oczywiście nie jest to zły pomysł, to po prostu niż w normalnym świecie Java jest zły pomysł (gdy chcesz tylko gettera lub setera). Na przykład niektóre frameworki (spring mvc) lub biblioteki już go używają (jstl) w ten sposób, niektóre parsery json lub xml go używają, jeśli chcesz użyć DSL, będziesz go używać. To zależy od scenariusza przypadku użycia.

W rzeczywistości nic nie jest dobre ani złe.

w rzeczywistości
źródło