Mechanizm refleksji

Z Wikipedii, wolnej encyklopedii
Skocz do: nawigacja, szukaj

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.

Przykłady[edytuj | edytuj kod]

Objective-C[edytuj | edytuj kod]

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 | edytuj kod]

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 | edytuj kod]

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 | edytuj kod]