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).
Ciekawym przykładem mechanizmu refleksji są języki programowania, w których każdy element kodu źródłowego reprezentowany jest strukturami danych dostępnymi dla programisty. Nazywamy je językami homoikonicznymi, a kod źródłowy ich programów jest w istocie danymi. Od decyzji programisty zależy, które jego fragmenty będą wartościowane, a które traktowane jak wartości stałe niewymagające obliczeń. Proces ten może być wielokierunkowy, np. część kodu źródłowego może zostać potraktowana jak dane, przekształcona, a następnie znów wartościowana – w ten sposób działają tzw. makra składniowe w dialektach języka Lisp.
Mechanizm refleksji jest najczęściej spotykany w językach wysokiego poziomu, zwykle opartych na maszynie wirtualnej.
Przykłady
C#
Poniższy przykład demonstruje użycie refleksji w języku C Sharp używając pakietu System.Reflection
//bez refleksji
Foo foo = new Foo();
foo.hello();
//z użyciem refleksji
var type = Type.GetType("namespace.Foo"); //string powinien zawierać namespace naszej klasy
var foo = Activator.CreateInstance(type); //inicjalizacja obiektu określonego typu
MethodInfo inf = type.GetMethod("hello");
inf.Invoke(foo); // jako drugi parametr metoda Invoke przyjmuje tablicę Object[] są to parametry metody hello.
Objective-C
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
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[] methodArr = c.getDeclaredMethods();
for (Method m : methodArr) {
out.print("Klasa " + className + " ma metode '" + m.getName() + "'");
out.println(" ktora zwraca wartosc typu " + m.getReturnType());
}
}
}
Ruby
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
- Java. Obiekty refleksyjne – artykuł wyjaśniający co to są refleksje i jak się je stosuje w Javie