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

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

В інформатиці, рефлексія — це процес, під час якого комп'ютерна програма може відслідковувати і модифікувати власну структуру і поведінку під час виконання.[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']()
// Без рефлексії
new Foo().hello();

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

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

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

// З рефлексією
QPushButton::staticMetaObject.className();  // "QPushButton"
-- Без рефлексії
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];
# Без рефлексії
my $foo = Foo->new();
$foo->hello();

# З рефлексією (використовуючи синтаксис розіменування об'єктів)
my $class  = "Foo";
my $method = "hello";
my $object = $class->new();
$object->$method();
//Без рефлексії
$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();
# Без рефлексії
Foo().hello()

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

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

Smalltalk

[ред. | ред. код]
"Без рефлексії"
Foo new hello

"З рефлексією "
((Smalltalk at: #Foo) perform: #new) perform: #hello
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.


Примітки

[ред. | ред. код]
  1. A Tutorial on Behavioral Reflection and its Implementation by Jacques Malenfant et al (PDF). Архів оригіналу (PDF) за 22 лютого 2016. Процитовано 2 червня 2015.
  2. Brian Cantwell Smith, Procedural Reflection in Programming Languages, Department of Electrical Engineering and Computer Science, Massachusetts Institute of Technology, PhD Thesis, 1982.
  3. Brian C. Smith. Reflection and semantics in a procedural language. Technical Report MIT-LCS-TR-272, Massachusetts Institute of Technology, Cambridge, Mass., January 1982. Архів оригіналу за 13 грудня 2015. Процитовано 2 червня 2015.

Література

[ред. | ред. код]
  • 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.