воскресенье, 12 августа 2007 г.

Что такое Inversion of Control?

Согласно этимологической справке, данной Мартином Фаулером, термин Inversion of Control (IoC) впервые был употреблен в статье Ральфа Джонсона и Брайана Фута «Designing reusable classes» в 1988 году. К сожалению, ни Фаулер, ни авторы упомянутой статьи не дают строгого определения этого термина, а предлагают его выводить из описания различия между такими понятиями как «библиотека» и «фреймворк». Вот что пишет Фаулер:

IoC – это то, чем определяется различие между фреймворком и библиотекой. По-существу, библиотека – это набор функций, которые можно вызывать из своей программы, сегодня обычно оформленный в виде классов. При каждом своем вызове функция проделывает некоторую работу и возвращает управление обратно клиенту.

Фреймворк, в свою очередь, определяет абстрактный прототип для некоторой задачи, с большим количеством встроенной логики. При использовании фреймворка программисту необходимо добавить логику своей частной задачи, как расширение, в различные области фреймворка с помощью наследования или подключения обработчиков. Код фреймворка самостоятельно вызывает пользовательский код в необходимые моменты.


Но, возможно, Фаулер дает самое точное из самого популярного определения. Если поискать в Google, то может сложиться впечатление, что IoC – это некоторый принцип, который характерен, во-первых, для объектно-ориентированного программирования (ООП), а, во-вторых, для больших программных компонентов (фреймворков). Более того, согласно статье в Википедии, термин IoC недостаточно четко определен, и многие попытки сделать это заканчиваются неудачей, так как нет четкого соглашения о сущности взаимодействия между объектом с одной стороны, и библиотекой, фреймворком или контейнером – с другой.

Я же считаю, что можно дать четкое определение того, что такое IoC. Более того, из определения будет видно, что IoC характерен не только для ООП и описания взаимодействия между классом и библиотекой или фреймворком, но и для любых стилей построения программ (процедурный, распределенный на основе сообщений), придерживающихся некоторого общего принципа.

Формальное определение IoC

Условия: Даны две сущности A и B, такие, что A зависит от B, но B не зависит от A. Такая зависимость означает, что сущность B может быть использована независимо от A, но A не может быть использована независимо от B. Рассматриваемыми сущностями могут быть классы, модули, библиотеки и любые другие программные сущности, которые экспортирую некоторое множество функций. Назовем экспортируемые множества функций Fa и Fb соответственно.

Определение IoC: Для данных в условии сущностей A и B, определяющих программу, существует обратный поток управления, если для некоторого набора входных параметров выполняются следующие условия.
  1. Выполнение программы начинается с некоторой функции x принадлежащей Fa .
  2. Существуют функции x и z принадл. Fa и некоторая функция y принадл. Fb, такие, что вызов функции x ведет к вызову функции y, которая, в свою очередь, вызывает функцию z. При этом функции x и z могут быть одной и той же функцией.
Пояснения и следствия

Необходимым условием наличия IoC является односторонняя зависимость сущности A от B, и соответственно множества функций Fa от Fb. Ведь только при наличии такой зависимости невозможен явный (прямой) вызов функции z принадл. Fa из функции y принадл. Fb. А так как прямой вызов невозможен, то такой вызов возможно осуществить только косвенно, каким-то образом передав адрес функции z в функцию y.

Однако не каждый косвенный вызов можно считать IoC. Так некоторая функция f может рекурсивно вызывать сама себя посредством косвенного вызова. Но это не будет считаться IoC, так как невозможно, чтобы одна функция входила в два непересекающихся множества.

Также не в каждом множестве функций может возникнуть ситуация обратной передачи управления. Рассмотрим полное множество Fb из определения, состоящее более чем из одной функции. И хотя возможен случай, когда данное множество возможно разделить на несколько непересекающихся подмножеств с односторонней зависимостью, я утверждаю, что ни одна – ни прямая, ни косвенная последовательность вызова этих функций не образует IoC. Так как множество функций заранее известно, то все косвенные вызовы всегда можно переписать в прямые. И только тогда, когда сущность B описывается так, что впоследствии ее может использовать некоторая сущность А, то невозможно переписать все косвенные вызовы в прямые, так как множество функций Fa в момент создания сущности B может быть неизвестно.

Невозможно также пройти стороной следующие два вопроса. А что, если множество Fb будет сконфигурировано так, что y будет вызывать не функцию z принадл. Fa, а некую функцию v принадл. Fb, либо w принадл. Fc? Можно ли такую ситуацию тоже назвать Inversion of Control? Я считаю, что если так поступить, то мы опять вернемся к тому, с чего начали – с вопроса о том, чем IoC отличается от косвенного вызова. Чтобы этого не произошло, будем считать, что IoC – это только такая ситуация, когда управление переходит в то множество, из которого был первоначальный вызов.

И так, можно говорить, что IoC образуется при связывании двух непересекающихся множеств функций Fa и Fb таким образом, что выполнение начинается с некоторой функции x принадл. Fa, передается в функцию y принадл. Fb из которой потом возвращается обратно в функцию z принадл. Fa. Реализуется посредством косвенного вызова или позднего связывания. При этом множество функций Fb специально реализовано так, чтобы быть впоследствии связанно с некоторым другим множеством функций.

В заключении хочу отметить, что согласно данному мной определению, такая стандартная и всем известная С-шная функция, как qsort реализована в полном соответствии с принципом Inversion of Control.

Комментариев нет: