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

Парадигмы программирования

Рефлексия (отражение; холоним интроспекции, англ. reflection) — процесс, во время которого программа может отслеживать и модифицировать собственную структуру и поведение во время выполнения. Парадигма программирования, положенная в основу рефлексии, является одной из форм метапрограммирования[1] и называется рефлексивным программированием.

Во время выполнения программных инструкций (кода) компьютеры обрабатывают данные, что приводит к их изменению, при этом компьютеры не изменяют код. Однако в большинстве современных компьютерных архитектур код хранится как данные, и в некоторых языках программирования реализована возможность обрабатывать собственный код как данные, что приводит к изменению уже самого кода во время его выполнения. Такие самоизменяющиеся программы в основном создаются с помощью высокоуровневых языков программирования, использующих виртуальные машины (например, Smalltalk, скриптовые языки). В меньшей степени рефлексия используется в языках с объявляемыми или статическими типами (например, Си, ML, Haskell, F#).

Понятие рефлексии в языках программирования введено Брайаном Смитом (Brian Cantwell Smith) в докторской диссертации 1982 года[2][3] наряду с понятием метациркулярного вычислителя (англ. Meta-circular evaluator) как компонента 3-Lisp.

Рефлексивно-ориентированное программирование

Рефлексивно-ориентированное программирование, или рефлексивное программирование, — функциональное расширение парадигмы объектно-ориентированного программирования. Рефлексивно-ориентированное программирование включает в себя самопроверку, самомодификацию и самоклонирование. Тем не менее главное достоинство рефлексивно-ориентированной парадигмы заключается в динамической модификации программы, которая может быть определена и выполнена во время работы программы. Некоторые императивные подходы, например процедурная и объектно-ориентированная парадигмы программирования, указывают, что существует четкая предопределённая последовательность операций обработки данных. Парадигма рефлексивно-ориентированного программирования, тем не менее, добавляет возможность динамической модификации программных инструкций во время работы и их вызова в модифицированном виде. То есть программная архитектура сама определяет, что именно можно делать во время работы исходя из данных, сервисов и специфических операций.

Применение

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

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

Примером, проясняющим преимущества рефлексии, может служить Сериализация объекта в JSON. Без рефлексии необходимо было бы явным образом указывать все имена полей класса и ссылаться на их значения для сериализации. Но рефлексия позволяет программе самой определить все имеющиеся поля и получить их текстовые имена. Таким образом, сериализация становится доступна для любого объекта без написания лишнего кода.

Реализации

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

  • поиск и модификация конструкций исходного кода (блоков, классов, методов, интерфейсов (протоколов) и т. п.) как объектов первого класса во время выполнения;
  • изменение имён классов и функций во время выполнения;
  • анализ и выполнение строк кода, поступающих извне;
  • создание интерпретатора байткода нового языка.

Реализованы эти возможности могуть быть разными путями. В языке MOO рефлексия является частью ежедневной идиомы программирования. Все вызываемые методы получают в контексте информацию о том, откуда они вызваны, и ссылки на объекты, к которым они принадлежат. Безопасность контролируется программно с помощью стека вызовов: вызывается callers() для получения списка методов; проверяется, не заблокировал ли callers()[1] сам себя.

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

Реализация рефлексии на языках, её не поддерживающих, выполняется с помощью системы трансформации программы для автоматического отслеживания изменений исходного кода.

Примеры

Пример на C#, в котором создаётся экземпляр foo класса Foo и осуществляется вызов метода Hello, не использующий рефлексию и использующий её:

// Без рефлексии
new Foo().Hello();

// С рефлексией
Type type = System.Type.GetType("Foo");
var foo = Activator.CreateInstance(type);
foo.GetType().GetMethod("Hello").Invoke(foo, null);

Аналогичный пример для ECMAScript, JavaScript и ActionScript:

// Без рефлексии
new Foo().hello()
 
// С рефлексией
 
// с допущением, что Foo лежит в this
new this['Foo']()['hello']()
 
// без допущений
new (eval('Foo'))()['hello']()

Примечания

Литература

  • 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.

Ссылки