Buch lesen: «Обратные вызовы в C++», Seite 13

Schriftart:

6.3.6. Обратные вызовы

Касательно обратных вызовов мы имеем следующую ситуацию. В системном API контекст вызова передается с помощью указателей на данные, по-другому организация передачи контекста здесь невозможна (см. п. 2.1.2). В интерфейсном классе указатель на данные не используется, поскольку в C++ имеется множество гораздо более изящных способов передачи контекста. Вот тут-то нам и понадобится перенаправление вызовов (см. п. 4.6.2). Реализация одной из интерфейсных функций API, использующей перенаправление вызовов, приведена в Листинг 110.

Листинг 110. Перенаправление вызовов в реализации интерфейсной функции (SensorLib.cpp)

ErrorCode readSensorValues(SensorValueCallback callback, void* pContextData)

{

  ErrorCode error = ERROR_NO;

  try

  {

    using namespace std::placeholders;

    g_SensorControl->readSensorValues(std::bind(callback,_1,_2,pContextData));   // (1)

  }

  catch (sensor::sensor_exception& e)

  {

    error = e.code();

  }

  return error;

}

В общем-то, вся реализация заключается в вызове метода интерфейсного класса (строка 1), в который вместо непосредственно обратного вызова передается объект связывания. Функция обратного вызова, объявленная в интерфейсе API, принимает 3 входных параметра: номер датчика, значение датчика и указатель на контекст. Когда будет происходить обратный вызов, то объект связывания вызовет назначенную функцию, в которую передаст первые два параметра исходной функции, а в третий параметр будет передан переданный указатель на контекст.

6.4. Итоги

На примере разработки модуля управления датчиками кратко описаны типовые этапы проектирования: описание технического задания; оформление сценариев функционирования системы; декомпозиция и формирование архитектуры. Затем рассмотрена реализация классов с акцентом на использовании обратных вызовов. И в заключение показан процесс создания системного API и трудности, с которыми сталкивается разработчик при реализации концепции «API как оболочка». Как можно увидеть в рассмотренном примере, в практике разработки ПО существует множество ситуаций, когда целесообразно использовать обратные вызовы как элементы дизайна компонентов системы.

Заключение

Итак, наше повествование подходит к концу, пора подвести некоторые итоги.

Обратный вызов – это паттерн, в котором какой-либо исполняемый код как аргумент передается в другой код. Ожидается, что через сохраненный аргумент исполняемый код будет запущен в какой-то момент времени. Типовые задачи, решаемые с помощью обратных вызовов, следующие: запрос данных; вычисления по запросу; перебор элементов; уведомления о событиях.

В C++ обратные вызовы реализуются с помощью следующих механизмов: указатель на функцию; указатель на статический метод класса; указатель на метод-член класса; функциональный объект; лямбда-выражение. Все они имеют свои достоинства и недостатки, и нельзя однозначно сказать, какой является наилучшим, все зависит от поставленных задач и требований к проектируемой системе.

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

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

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

Уникальная особенность обратных вызовов проявляется в том, что они дают возможность динамической модификации поведения программы во время выполнения. Это обуславливает их широкое применение на практике, как было продемонстрировано в примере разработки модуля управления датчиками.

Список литературы и интернет-источников

Здесь приводится список упомянутых книг, а также литература и интернет-ресурсы для углубленного изучения рассмотренных тем.

1. Басс Л., Клементс П., Кацман Р. Архитектура программного обеспечения на практике. Спб, Питер, 2006. – 574 с.

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

2. Вандевурд Д., Джосаттис Н., Грегор Д. Шаблоны C++. Справочник разработчика. Спб, Альфа-книга, 2018. – 848 с.

Максимально полно охватывает разнообразные аспекты использования шаблонов в C++, подходит как как в качестве справочного, так и учебного пособия.

3. Галовиц Я. C++ 17 STL. Стандартная библиотека шаблонов. Спб., Питер, 2018. – 432 с.

Отличная книга для изучения стандартной библиотеки STL.

4. Гамма Э., Хелм Р., Джонсон Р., Влиссидес Д. «Приемы объектно-ориентированного проектирования. Паттерны проектирования». Спб, Питер, 2020. – 368 с.

Рассматриваются наиболее употребительные паттерны проектирования и их использование в решении задач. Не привязана к конкретному языку программирования. Отличается легкостю и доступностью изложения.

5. Касперски Крис. Техника оптимизации программ. Эффективное использование памяти. Спб, БХВ-Петербург, 2003. – 560 с.

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

6. Леоненков А. В. Самоучитель UML 2. СПб, БХВ-Петербург, 2007. – 576 с.

Простое и доступное изложение основ UML.

7. Орлов С. А. Программная инженерия. Технологии разработки программного обеспечения. Спб, Питер, 2018. – 640 с.

Рассматривается методология разработки программного обеспечения, организации и процессы проектирования больших программных систем.

8. Пикус Ф. Г. Идиомы и паттерны проектирования в современном C++. М, ДМК Пресс, 2020. – 452 с.

Рассматриваются реализации различных паттернов проектирования с использованием современных средств C++. Книга достаточно сложная, предполагается, что читатель хорошо владеет C++, имеет опыт обобщенного программирования.

9. Пирс Бенджамин. Типы в языках программирования. М., Лямбда-пресс, 2011. – 656 с.

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

10. Эванс Эрик. Предметно-ориентированное проектирование (DDD): структуризация сложных программных систем. М., Вильямс, 2011. – 448 с.

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

11. Lambda Expressions in C++.

https://docs.microsoft.com/en-us/cpp/cpp/lambda-expressions-in-cpp?view=vs-2019

Кратко и наглядно описан синтаксис лямбда-выражений.

12. Template Specialization (C++).

https://docs.microsoft.com/en-us/cpp/cpp/template-specialization-cpp?view=vs-2019

Множество развернутых примеров, демонстрирующих использование частичной специализацию шаблонов.