Mechanizm refleksji
Mechanizm refleksji – pojęcie z dziedziny informatyki oznaczające proces, dzięki któremu program komputerowy może być modyfikowany w trakcie działania w sposób zależny od własnego kodu oraz od zachowania w trakcie wykonania. Paradygmat programowania ściśle związany z mechanizmem refleksji to programowanie refleksyjne.
Refleksja pozwala w łatwy sposób zarządzać kodem tak, jakby był danymi. Używa się jej najczęściej do zmieniania standardowego zachowania już zdefiniowanych metod lub funkcji, a także do tworzenia własnych konstrukcji semantycznych modyfikujących język. Z drugiej strony kod wykorzystujący refleksję jest mniej czytelny i nie pozwala na sprawdzenie poprawności składniowej i semantycznej w trakcie kompilacji (niewygodne śledzenie błędów).
Mechanizm ten jest częściej spotykany w językach wysokiego poziomu, zwykle opartych na maszynie wirtualnej.
Spis treści |
Przykłady [edytuj]
Objective-C [edytuj]
Poniższy przykład demonstruje użycie refleksji w języku Objective-C
// bez refleksji Foo *foo = [[Foo alloc] init]; [foo hello]; [foo release]; // z refleksją id foo = [[NSClassFromString(@"Foo") alloc] init]; SEL selector = NSSelectorFromString(@"hello"); [foo performSelector:selector]; [foo release];
Java [edytuj]
Poniższy przykład w języku Java wykorzystuje pakiet java.lang.reflect.
// bez refleksji Foo foo = new Foo(); foo.hello(); // z refleksją Class cl = Class.forName("Foo"); Method method = cl.getMethod("hello"); method.invoke(cl.newInstance());
Oba fragmenty tworzą instancję klasy Foo, następnie wywołują metodę hello() tej klasy. Różnica polega na tym, że w pierwszym fragmencie nazwa klasy i metody są częścią kodu źródłowego, podczas gdy w drugim fragmencie możliwe jest przeniesienie ich do zmiennych, których wartość jest ustalana w czasie wykonania kodu.
Mechanizm refleksji pozwala także na zdobywanie informacji o klasach w trakcie wykonania programu. W poniższym przykładzie Klasa Main sprawdza jaki jest typ zwracany przez metody klasy Bar.
public class Bar { public String fun(Integer i) { return "0" + i + ", zglos sie!"; } } import static java.lang.System.out; import java.lang.reflect.*; public class Main { public static void main(String[] args) throws Exception { String className = "Bar"; Class c = Class.forName(className); Method[] m = c.getDeclaredMethods(); for (int i=0;i<m.length;++i) { out.print("Klasa " + className + " ma metode '" + m[i].getName().toString() + "'"); out.println(" ktora zwraca wartosc typu " + m[i].getReturnType().toString()); } } }
Ruby [edytuj]
Przykład w języku Ruby, który dodaje metodę klasową once, pozwalającą zaznaczyć, że dana funkcja składowa klasy ma być wykonywana tylko raz. Podprogram modyfikuje kod oznaczonych metod w taki sposób, że nadaje im nową nazwę. Pod starą nazwą umieszcza nową metodę, która buforuje wartość zwracaną przez pierwotnie zdefiniowaną funkcję, tym samym pozwalając się jej wykonać tylko raz.
# part of date.rb - date and time library # Author: Tadayoshi Funaba 1998-2008 class Date class << self def once(*ids) # :nodoc: -- restricted for id in ids module_eval <<-"end;" alias_method :__#{id.object_id}__, :#{id.to_s} private :__#{id.object_id}__ def #{id.to_s}(*args) @__ca__[#{id.object_id}] ||= __#{id.object_id}__(*args) end end; end end private :once end end
Inny przykład to rozszerzenie możliwości języka o konstrukcję automatycznie kasującą zawartość wskazanych przez programistę buforów, jeśli uruchomione zostaną wyszczególnione metody. Zadaniem metaprogramu jest tu również opakowanie metod, jednak zapamiętywane są identyfikatory ich obiektów a nie identyfikatory obiektów ich symbolicznych nazw. Metaprogram zawarto w przykładowym module BufferAffects, który można pobrać z serwisu GitHub. Domieszkując ten moduł możemy korzystać z dodatkowych metod klasowych pozwalających na stosowanie w kodzie klauzul buffers_reset_method i attr_affects_buffers:
require 'bufferaffects' # http://gist.github.com/88178 class Main # domieszkowanie modułu extend BufferAffects # metoda opróżniająca wykorzystywany bufor buffers_reset_method :reset_path_buffer # pola które po zmianie powinny wpływać na # zawartość bufora attr_affects_buffers :subpart # standardowe akcesory pól attr_accessor :subpart, :otherpart # metoda opróżniająca bufor def reset_path_buffer(name) @path = nil p "uruchomiono reset dla #{name}" end # metoda z buforowanym wyjściem def path @path ||= @subpart.to_s + @otherpart.to_s end end # tworzenie nowego obiektu obj = Main.new # ustawianie jednego z pól # i wyświetlanie buforowanych wynikow obj.subpart = 'test' p obj.path obj.subpart = '1234' p obj.path
Linki zewnętrzne [edytuj]
- Java. Obiekty refleksyjne – artykuł wyjaśniający co to są refleksje i jak się je stosuje w Javie