Простота . Показывает, насколько просто и быстро можно написать, отладить и сопровождать код.
Независимость компонентов . Показывает, нужно ли изменять код одного компонента при изменении другого. Чем меньше зависимости между компонентами (в нашем случае это инициатор и исполнитель), тем проще разработка и отладка программной системы. Кроме того, упрощается ее сопровождение и повышается надежность.
Отсутствие трансляции контекста . Отсутствие необходимости трансляции контекста упрощает разработку, улучшает прозрачность кода и повышает независимость компонентов. И наоборот, трансляция контекста усложняет код и заставляет инициатор выполнять дополнительные операции для хранения и передачи контекста
Безопасность . Показывает устойчивость системы к потенциальным ошибкам.
Гибкость . Показывает, насколько просто модифицировать код при появлении новых требований.
Полиморфизм . Показывает, поддерживается ли полиморфизм в реализации исполнителя. Поддержка полиморфизма упрощает разработку и повышает гибкость в рамках объектно-ориентированной парадигмы.
Быстродействие . Показывает, насколько быстро осуществляется вызов кода исполнителя.
Системный API . Показывает возможность реализации системных API.
C++ API . Показывает возможность реализации C++ API.
Итак, объекты анализа выбраны, критерии определены. Теперь нужно построить матрицу соответствия. Для начала мы будем использовать качественный анализ, поскольку он более простой в реализации.
3.2.1. Матрица соответствия
Матрица соответствия строится в виде таблицы. В строках выписываются требования, в столбцах – способы реализации, в ячейках – признаки, указывающие, насколько реализация поддерживает соответствующий критерий (Табл. 7.)
Табл. 7. Качественный анализ реализаций обратных вызовов
Легенда: ▪ полностью поддерживается; ▫ поддерживается частично; пустое поле – не поддерживается
По каким соображениям мы назначили оценки?
Простота . Самой сложной реализацией будет, пожалуй, указатель на метод-член класса: запутанный и не слишком наглядный синтаксис. Довольно сложной выглядит реализация лямбда-выражений, поскольку приходится использовать шаблоны. Несколько проще выглядит реализация с помощью указателей на функцию, но там немного запутывает необходимость приведения типов. На этом фоне остальные реализации выглядят достаточно простыми.
Независимость компонентов . Полностью независимыми будет реализация с помощью указателей на функцию: как бы мы не модифицировали код исполнителя, как бы не меняли используемый контекст, код инициатора остается неизменным, даже не требуется его перекомпиляция. Это одна из причин, почему указанная реализация подходит для построения системных API. Лямбда-выражения являются относительно независимыми: при любом изменении состава и типов захваченных переменных код инициатора остается неизменным, но он будет требовать перекомпиляции, поскольку реализован с использованием шаблонов. Указатели на методы классов являются частично независимыми, поскольку требуют предварительного объявления класса в инициаторе. Использование функциональных объектов порождает монолитную архитектуру, где инициатор и исполнитель зависят друг от друга.
Отсутствие трансляции контекста . Указатели на функции и статические методы требуют трансляции контекста, остальные реализации этого не требуют.
Безопасность . Самыми безопасными являются функциональные объекты и лямбда- выражения, потому что в инициаторе хранятся их копии, никак не зависящие от исполнителя. Указатели на методы класса поддерживают безопасность лишь частично: управление временем жизни экземпляра класса возлагается на исполнителя, и потенциально возможны ситуации, когда последний уничтожает экземпляр класса, указатель на который остается в инициаторе и может быть вызван. Указатель на функцию не является безопасным, поскольку исполнитель интерпретирует контекст приведением типов, и нет никакой возможности проверить полученный указатель.
Гибкость . Самым гибким является указатель на метод класса, поскольку здесь имеются несколько способов модификации поведения обработчика. Другие реализации не предлагают таких возможностей, а функциональные объекты в силу монолитной структуры гибкими не являются.
Читать дальше