Рефлексія (програмування)

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до навігації Перейти до пошуку

В інформатиці, рефлексія — це процес, під час якого комп'ютерна програма може відслідковувати і модифікувати власну структуру і поведінку під час виконання.[1]

Це, кажучи простими словами, спосіб звернення програми до власного коду.

Історія[ред. | ред. код]

Перші комп'ютери були запрограмовані за допомогою їхньої нативної мови асемблер. Рефлексивність у ній була спадковою, оскільки ранні комп'ютери могли бути запрограмовані через визначення виконуваних інструкцій як даних та з використанням самомодифікуючого коду. Коли програмування дійшло до високорівневих мов програмування, таких як C, рефлексивна здатність зникнула (за межами Шкідливого програмного засобу), поки не з'явилися мови програмування з вбудованою рефлексією в їхні системи типізації.

Поняття рефлексії в мовах програмування ввів Браян Кентвелл Сміт[en] в докторській дисертації 1982 р.[2][3]

Використання[ред. | ред. код]

Рефлексія може використовуватися для спостерігання і зміни програми під час виконання. Рефлексивний компонент програми може спостерігати за виконанням певної частини коду і змінювати себе для досягнення потрібної цілі. Модифікація виконується під час виконання програми шляхом динамічної зміни коду.

В об'єктно-орієнтованих мовах програмування, таких як Java, рефлексія дозволяє огляд класів, інтерфейсів, полів і методів в період виконання програми, не знаючи імен інтерфейсів, полів, методів в період компіляції. Вона також дозволяє викликати методи.

Рефлексію можна застосовувати для динамічної адаптації програми до різноманітних ситуацій. Наприклад, розглянема програму, яка використовує два різні класи X і Yдля виконання аналогічних операцій. Без рефлексії в коді програми методи класів X і Y будуть викликатися явно. Якщо програма спроектована з використанням рефлексно-орієнтованої парадигми програмування, деяка ділянка коду не буде містити явних викликів методів класів X і Y; програма виконає цю частину двічі: спочатку для класу X, потім для класу Y.

Рефлексія — це один з видів метапрограмування.

В деяких об'єктно-орієнтованих мовах програмування, таких як C# і Java, рефлексію можна використовувати для перевизначення правил доступу до методів класу. Наприклад, за допомогою рефлексії можна змінити значення поля з модифікатором доступу private з стороннього класу.

Реалізація[ред. | ред. код]

Програми, котрі написані за допомогою тих мов програмування, які підтримують рефлексію, мають додаткові можливості, реалізація яких на низькорівневих мовах є доволі важкою. Перерахуємо деякі з них:

  • пошук і модифікація конструкцій початковий коду (блоків, класів, методів, протоколів і т.д.) як об'єктів першого класу під час виконання;
  • зміна імен класів і функцій під час виконання;
  • аналіз і виконання стрічок коду, які надходять;
  • створення інтерпретатора псевдокоду нової мови.

Ці можливості можна реалізовувати різними способами. У мові MOO рефлексія є частиною щоденної ідіоми програмування. Всі методи, які викликаються, отримують в контексті інформацію про те, звідки вони були викликані, і посилання на об'єкти, до яких вони належать. Безпека контролюється програмно за допомогою стеку викликів: викликається callers() для отримання списку методів; перевіряється, чи не заблокував callers() сам себе.

Компільовані мови покладаються на свої системи виконання, які забезпечують програми інформацією про їхній початковий код. Скомпільований на Objective-C виконуваний файл, наприклад, записує імена всіх методів в один блок, створює таблицю відповідності. В компільованих мовах, які підтримують створення функцій під час виконання, таких як Common Lisp, середовище виконання має містити компілятор і інтерпретатор.

Реалізація рефлексії в мовах, які її не підтримують, виконується за допомогою системи трансформації програми для автоматичного відслідковування змін в початковому коді.

Приклади[ред. | ред. код]

Наступні приклади показують застосування рефлексії на прикладі створення екземпляру foo класу Foo і виклику метода hello (абоHello) в різних мовах програмування. Для кожної мови наведено два приклади: у першому не використовується рефлексія, а у другому використовується.

ECMAScript[ред. | ред. код]

Так само працює JavaScript і ActionScript:

//Без рефлексії
new Foo().hello()
 
// З рефлексією
 
// assuming that Foo resides in this
new this['Foo']()['hello']()
 
// or without assumption
new (eval('Foo'))()['hello']()

Java[ред. | ред. код]

// Без рефлексії
new Foo().hello();

// З рефлексією
Class foo = Class.forName("Foo");
foo.getMethod("hello", null).invoke(foo.newInstance(), null);

Qt/C++[ред. | ред. код]

Бібліотека Qt розширює можливості C++ за допомогою метамови й забезпечує підтримку рефлексії для посилання на члени/методи класу і запит імені об'єктів Qt за допомогою QMetaObject, що містить метадані про об'єкти Qt.

// Без рефлексії
QObject *obj = new QPushButton;
obj->metaObject()->className();             // "QPushButton"

// З рефлексією
QPushButton::staticMetaObject.className();  // "QPushButton"

Lua[ред. | ред. код]

-- Без рефлексії
Foo.hello()
 
-- З рефлексією
_G['Foo']['hello']()

Objective-C[ред. | ред. код]

// Без рефлексії
Foo *foo = [[Foo alloc] init];
[foo hello];

// З рефлексією
Class cls = NSClassFromString(@"Foo");
id foo = [[cls alloc] init];
SEL selector = NSSelectorFromString(@"hello");
[foo performSelector:selector withObject:nil];

Perl[ред. | ред. код]

# Без рефлексії
my $foo = Foo->new();
$foo->hello();

# З рефлексією (використовуючи синтаксис розіменування об'єктів)
my $class  = "Foo";
my $method = "hello";
my $object = $class->new();
$object->$method();

PHP[ред. | ред. код]

//Без рефлексії
$oFoo = new Foo();
$oFoo->hello();

//З рефлексією (використовуючи ReflectionClass)
$oReflector = new ReflectionClass('Foo');
$oFoo = $oReflector->newInstance();
$oHello = $oReflector->getMethod('hello');
$oHello->invoke($oFoo);

//З рефлексією (використовуючи callback)
$oFoo = new Foo();
call_user_func(array($oFoo,'hello'));

//З рефлексією (використовуючи синтаксис розіменування об'єктів)
$class_name = "Foo";
$f = new $class_name();
$method = "hello";
$f->$method();

Python[ред. | ред. код]

# Без рефлексії
Foo().hello()

# З рефлексією 
getattr(globals()['Foo'](), 'hello')()

Ruby[ред. | ред. код]

# Без рефлексії
Foo.new.hello

# З рефлексією 
Object.const_get(:Foo).send(:new).send(:hello)

Smalltalk[ред. | ред. код]

"Без рефлексії"
Foo new hello

"З рефлексією "
((Smalltalk at: #Foo) perform: #new) perform: #hello

Io[ред. | ред. код]

Foo := Object clone do(
    hello := method(
        "Hello" println
    )
)

# Без рефлексії
Foo hello

# З рефлексією 
getSlot("Foo") getSlot("hello") call

ActionScript 3.0[ред. | ред. код]

// Без рефлексії
var foo:Foo = new Foo();
foo.hello();

// З рефлексією 
var cls:Object = getDefinitionByName("Foo");
var foo:Object = new cls();
foo["hello"]();

Delphi 2010[ред. | ред. код]

// Без рефлексії
var
  foo : TFoo;
begin
  foo := TFoo.Create();
  foo.Hello();
end;

// З рефлексією 
var
 c : TRttiContext;
 t : TRttiInstanceType;
 foo : TValue;
begin
   c := TRttiContext.Create;
   t := (c.FindType('TFoo') as TRttiInstanceType);
   foo := t.GetMethod('Create').Invoke(t.MetaclassType,[]);
   t.GetMethod('Hello').Invoke(foo,[]);
   c.Free;
end.


Примітки[ред. | ред. код]

Література[ред. | ред. код]

  • Forman, Ira R. and Forman, Nate. Java Reflection in Action. — Manning Publications Co, 2004. — ISBN 1932394184.
  • Forman, Ira R. and Danforth, Scott H. Putting Metaclasses to Work: A New Dimension in Object-oriented Programming. — Addison Wesley Longman Publishing Co., Inc, 1999. — ISBN 0-201-43305-2.