Nie wiem dlaczego, ale zawsze czuję, że „oszukuję”, kiedy używam refleksji - może to z powodu przeboju wydajności, o którym wiem, że biorę.
Część mnie mówi, że jeśli jest to część języka, którego używasz i może osiągnąć to, co próbujesz zrobić, to dlaczego nie użyć tego. Druga część mnie mówi, że musi istnieć sposób, aby to zrobić bez użycia refleksji. Chyba może to zależy od sytuacji.
Jakie są potencjalne problemy, na które muszę zwrócić uwagę podczas korzystania z refleksji i jak mam się nimi martwić? Ile wysiłku warto poświęcić na znalezienie bardziej konwencjonalnego rozwiązania?
Odpowiedzi:
Nie, to nie jest oszukiwanie - to sposób na rozwiązanie problemów w niektórych językach programowania.
Obecnie często nie jest to najlepsze (najczystsze, najprostsze, najłatwiejsze w utrzymaniu) rozwiązanie. Jeśli istnieje lepszy sposób, skorzystaj z niego. Czasami jednak nie ma. A jeśli tak, to jest to o wiele bardziej skomplikowane, wymagające dużego powielania kodu itp., Co czyni go niemożliwym (trudnym do utrzymania na dłuższą metę).
Dwa przykłady z naszego obecnego projektu (Java):
fieldX
do odpowiedniego pola w klasie i zainicjować ten ostatni. W niektórych przypadkach może zbudować proste okno dialogowe GUI na podstawie zidentyfikowanych właściwości w locie. Bez refleksji zajęłoby to setki wierszy kodu w kilku aplikacjach. Tak więc refleksja pomogła nam szybko stworzyć proste narzędzie bez większego zamieszania i pozwoliła nam skupić się na ważnej części (testowanie regresji naszej aplikacji internetowej, analiza dzienników serwera itp.), A nie na nieistotnym.Najważniejsze jest, jak każde potężne narzędzie, również odbicie może być użyte do strzelania sobie w stopę. Jeśli nauczysz się, kiedy i jak (nie) z niego korzystać, może przynieść ci eleganckie i czyste rozwiązania trudnych problemów. Jeśli go nadużyjesz, możesz zmienić prosty problem w złożony i brzydki bałagan.
źródło
if (propName = n) setN(propValue);
, możesz nazwać swoje (tj.) Tagi XML tak samo jak właściwości kodu i uruchomić nad nimi pętlę. Ta metoda ułatwia także późniejsze dodawanie właściwości.To nie oszustwo. Ale zwykle jest to zły pomysł w kodzie produkcyjnym z co najmniej następujących powodów:
Sugeruję ograniczenie użycia refleksji do następujących przypadków:
We wszystkich innych przypadkach sugeruję wymyślenie podejścia, które pozwala uniknąć refleksji. Zdefiniowanie interfejsu za pomocą odpowiednich metod i wdrożenie go na zbiorze klas, w których chcesz wywołać metodę (metody), zwykle wystarcza do rozwiązania najprostszych przypadków.
źródło
Refleksja to kolejna forma metaprogramowania, równie ważna, jak parametry oparte na typie, które można obecnie zobaczyć w większości języków. Odbicie jest potężne i ogólne, a programy odblaskowe charakteryzują się wysokim stopniem łatwości konserwacji (oczywiście przy prawidłowym stosowaniu), a nawet więcej niż programy czysto obiektowe lub proceduralne. Tak, płacisz cenę za wydajność, ale chętnie wybrałbym wolniejszy program, który jest łatwiejszy w utrzymaniu w wielu, a nawet większości przypadków.
źródło
Na pewno wszystko zależy od tego, co próbujesz osiągnąć.
Na przykład napisałem aplikację do sprawdzania multimediów, która wykorzystuje wstrzykiwanie zależności w celu ustalenia, jaki rodzaj mediów (pliki MP3 lub JPEG) należy sprawdzić. Powłoka musiała wyświetlić siatkę zawierającą istotne informacje dla każdego typu, ale nie wiedziała, co zamierza wyświetlić. Jest to zdefiniowane w zestawie, który czyta ten typ nośnika.
Dlatego musiałem użyć refleksji, aby uzyskać liczbę kolumn do wyświetlenia oraz ich typy i nazwy, aby poprawnie ustawić siatkę. Oznaczało to również, że mogłem zaktualizować wstrzykiwaną bibliotekę (lub utworzyć nową) bez zmiany innego kodu lub pliku konfiguracyjnego.
Jedynym innym sposobem byłoby posiadanie pliku konfiguracyjnego, który musiałby zostać zaktualizowany po zmianie rodzaju sprawdzanego nośnika. Wprowadziłoby to kolejny punkt niepowodzenia dla aplikacji.
źródło
Refleksja to niesamowite narzędzie, jeśli jesteś autorem biblioteki , a zatem nie ma wpływu na przychodzące dane. Połączenie refleksji i metaprogramowania pozwala twojej bibliotece płynnie współpracować z dowolnymi wywołującymi, bez konieczności przeskakiwania przez generowanie kodu itp.
Staram się jednak zniechęcać do refleksji w kodzie aplikacji ; w warstwie aplikacji powinieneś używać różnych metafor - interfejsów, abstrakcji, enkapsulacji itp.
źródło
Refleksja jest fantastyczna do tworzenia narzędzi dla programistów.
Ponieważ pozwala środowisku kompilacji na sprawdzenie kodu i potencjalne wygenerowanie odpowiednich narzędzi do manipulowania / inicjowania kontroli kodu.
Jako ogólna technika programowania może być użyteczna, ale jest bardziej krucha, niż większość ludzi sobie wyobraża.
Jednym z naprawdę przydatnych zastosowań do refleksji (IMO) jest to, że sprawia, że pisanie ogólnej biblioteki strumieniowej jest bardzo proste (o ile opis klasy nigdy się nie zmienia (wtedy staje się bardzo kruchym rozwiązaniem)).
źródło
Użycie refleksji jest często szkodliwe w językach OO, jeśli nie jest używane z dużą świadomością.
Nie pamiętam, ile złych pytań widziałem na stronach StackExchange, gdzie
Tutaj są typowymi przykładami.
Najważniejsze jest to, że OO
Jeśli w którymkolwiek momencie kodu punkt 2 nie jest poprawny dla przekazanego obiektu, to co najmniej jeden z nich jest prawdziwy
Słabo wykwalifikowani programiści po prostu tego nie rozumieją i wierzą, że można im przekazać dowolną część kodu i zrobić to, co chcą z (zakodowanego na stałe) zestawu możliwości. Ci idioci często używają refleksji .
W przypadku języków OO odbicie powinno być potrzebne tylko w przypadku działania meta (moduły ładujące klasy, wstrzykiwanie zależności itp.). W tych kontekstach potrzebna jest refleksja, ponieważ zapewniasz ogólną usługę wspomagającą manipulację / konfigurację kodu, o której nic nie wiesz z dobrego i uzasadnionego powodu. W prawie każdej innej sytuacji, jeśli sięgasz po refleksję, robisz coś złego i musisz zadać sobie pytanie, dlaczego ten fragment kodu nie wie wystarczająco dużo o przekazanym mu obiekcie.
źródło
Alternatywą, w przypadkach, gdy domena klas odbijanych jest dobrze zdefiniowana, jest użycie odbicia wraz z innymi metadanymi do wygenerowania kodu , zamiast używania odbicia w czasie wykonywania. Robię to za pomocą FreeMarker / FMPP; istnieje wiele innych narzędzi do wyboru. Zaletą tego jest to, że powstaje „prawdziwy” kod, który można łatwo debugować itp.
W zależności od sytuacji może to pomóc w stworzeniu o wiele szybszego kodu - lub po prostu rozlaniu kodu. Pozwala uniknąć wad refleksji:
wspomniano wcześniej.
Jeśli refleksja wydaje się być oszustwem, może być tak, ponieważ opierasz się na wielu domysłach, których nie jesteś pewien, a twoje jelita ostrzegają, że jest to ryzykowne. Pamiętaj, aby zapewnić sposób na ulepszenie metadanych nieodłącznie odzwierciedlonych za pomocą własnych metadanych, w którym możesz opisać wszystkie dziwactwa i specjalne przypadki klas, które możesz napotkać.
źródło
To nie jest oszustwo, ale jak każde narzędzie, powinno być używane do tego, co ma rozwiązać. Refleksja z definicji pozwala ci sprawdzać i modyfikować kod poprzez kod; jeśli to właśnie musisz zrobić, to odbicie jest narzędziem do pracy. Refleksja dotyczy przede wszystkim meta-kodu: kodu, który celuje w kod (w przeciwieństwie do zwykłego kodu, który celuje w dane).
Przykładem dobrego zastosowania refleksji są ogólne klasy interfejsu usługi sieciowej: Typowym rozwiązaniem jest oddzielenie implementacji protokołu od funkcjonalności ładunku. Mamy więc jedną klasę (nazwijmy ją
T
), która implementuje ładunek, a drugą, która implementuje protokół (P
).T
jest dość proste: dla każdego połączenia, które chcesz wykonać, po prostu napisz jedną metodę, która zrobi wszystko, co powinna.P
musi jednak mapować wywołania usługi internetowej na wywołania metod. Uczynienie tego mapowania ogólnym jest pożądane, ponieważ pozwala uniknąć nadmiarowości i sprawia, że można goP
wielokrotnie używać. Refleksja zapewnia środki do sprawdzania klasyT
w czasie wykonywania i wywoływania jej metod na podstawie ciągów przekazywanychP
przez protokół usługi sieci Web, bez wiedzy klasy na temat kompilacjiT
. Stosując zasadę „kod o kodzie” można argumentować, że klasaP
ma kod w klasieT
jako część swoich danych.Jednak.
Refleksja daje również narzędzia do obejścia ograniczeń systemu typów językowych - teoretycznie można przekazać wszystkie parametry jako typ
object
i wywołać ich metody poprzez refleksje. Voilà, język, który ma wymuszać silną dyscyplinę pisania statycznego, zachowuje się teraz jak język dynamicznie pisany z późnym wiązaniem, tyle że składnia jest znacznie bardziej rozbudowana. Każde wystąpienie takiego wzoru, które do tej pory widziałem, było brudnym włamaniem i niezmiennie możliwe byłoby rozwiązanie w systemie typów językowych, które byłoby bezpieczniejsze, bardziej eleganckie i wydajniejsze pod każdym względem .Istnieje kilka wyjątków, takich jak kontrolki GUI, które mogą być powiązane z różnymi niepowiązanymi typami źródeł danych; nakazanie, aby Twoje dane implementowały określony interfejs tylko po to, abyś mógł go powiązać, nie jest realistyczne, a programista nie implementuje adaptera dla każdego typu źródła danych. W takim przypadku bardziej przydatne jest zastosowanie odbicia w celu wykrycia rodzaju źródła danych i dostosowanie powiązania danych.
źródło
Jednym z problemów, jakie mieliśmy z Reflection, jest dodanie Obfuscation do miksu. Wszystkie klasy otrzymują nowe nazwy i nagle ładowanie klasy lub funkcji po nazwie przestaje działać.
źródło
To całkowicie zależy. Przykładem czegoś, co byłoby trudne do zrobienia bez refleksji, jest replikacja ObjectListView . Generuje również kod IL w locie.
źródło
Refleksja jest podstawową metodą tworzenia systemów opartych na konwencjach. Nie zdziwiłbym się, gdybym był często używany w większości platform MVC. Jest to główny składnik ORM. Możliwe, że już używasz komponentów zbudowanych z niego każdego dnia.
Alternatywą dla takiego zastosowania jest konfiguracja, która ma swój własny zestaw wad.
źródło
Refleksja może osiągnąć rzeczy, których po prostu nie da się zrobić praktycznie inaczej.
Na przykład zastanów się, jak zoptymalizować ten kod:
Pośrodku wewnętrznej pętli znajduje się kosztowny klaps testowy, ale wyodrębnienie go wymaga przepisania pętli raz dla każdego operatora. Refleksja pozwala nam uzyskać wydajność na równi z wyodrębnieniem tego testu, bez powtarzania pętli kilkanaście razy (a tym samym poświęcając łatwość konserwacji). Wystarczy wygenerować i skompilować potrzebną pętlę w locie.
Tak naprawdę dokonałem tej optymalizacji , chociaż sytuacja była nieco bardziej skomplikowana, a wyniki były niesamowite. Poprawa wydajności o rząd wielkości i mniejsza liczba wierszy kodu.
(Uwaga: początkowo próbowałem przekazać odpowiednik Func zamiast char, i było to nieco lepsze, ale nie prawie 10-krotnie uzyskane odbicie).
źródło
Nie ma mowy, żeby oszukiwał ... Zamiast tego umożliwia serwerom aplikacji prowadzenie klas stworzonych przez użytkownika o wybranej przez siebie nazwie, zapewniając tym samym elastyczność użytkownika, a nie oszukiwanie go.
A jeśli chcesz zobaczyć kod dowolnego pliku .class (w Javie), istnieje kilka darmowych dekompilatorów!
źródło