Обратные вызовы в C++ - [2]
Итак, результат один и тот же, но последовательность действий различная. В первом случае вы сами идете в банк, отстаиваете очередь и совершаете платеж, т. е. выполняете все необходимые операции. Во втором случае вы сидите и ожидаете, когда вам позвонят, т. е. сделают вызов, и делаете только одно действие, а именно – совершаете платеж. Либо это делает ваш друг, если маме (или бабушке) дали его, а не ваши контакты. Можно утверждать, что ваша мама (или бабушка) инициировала, а вы выполнили обратный вызов.
1.1.2. Обратный вызов как паттерн
Перейдем теперь на язык программирования и дадим формальное определение.
Обратный вызов – это паттерн, в котором какой-либо исполняемый код как аргумент передается в другой код, при этом ожидается, что через сохраненный аргумент исполняемый код будет запущен в требуемый момент времени.
Возвращаясь к неформальному примеру: здесь выполнение платежа можно считать исполняемым кодом, номер телефона – аргументом, телефонный звонок – запуском кода на выполнение.
Графически описанную концепцию можно проиллюстрировать следующим образом (Рис. 1). В программе существует код, выполняющий какие-либо операции, или исполняемый код. Когда программа запускается, исполняемый код как аргумент передается в другой код, или вызывающий код. Вызывающий код сохраняет переданный аргумент и начинает работу. В нужный момент времени, используя сохраненный аргумент, вызывающий код запускает исполняемый код, т. е. осуществляет обратный вызов.
Рис. 1. Концепция обратных вызовов
1.1.3. Прямые и обратные вызовы
Различие между прямым и обратным вызовом проиллюстрировано на Рис. 2. В первом случае поток управления запускает вызывающий код, из которого вызывается исполняемый код, и далее управление возвращается в точку вызова. Во втором случае поток управления идет мимо исполняемого кода и настраивает аргумент в вызывающем коде, а вызов исполняемого кода осуществляет уже вызывающий код, т. е. поток управления идет в обратном направлении. Таким образом, мы имеем обратный вызов.
Рис. 2. Прямой и обратный вызов
1.2. Задачи, решаемые с помощью обратных вызовов
Все многообразие задач, решаемых с помощью обратных вызовов, можно разделить на следующие группы.
1.2.1. Запрос данных
Представим, что мы разрабатываем программное обеспечение для микроконтроллера управления технологическими процессами. Контроллеру требуется периодически получать показания датчиков, таких как температура, влажность, давление и т. д. Как это реализовать?
Самое простое решение – код для опроса датчиков непосредственно реализовать в ПО контроллера. Но здесь возникает множество вопросов. А если в системе понадобится использовать другую модель датчика, код опроса которого должен быть другим? А если нам нужно использовать различные датчики для различных режимов? А как быть, когда мы вообще не знаем, какие датчики будут использоваться?
Эффективный способ решения указанных проблем – разработка драйвера, т. е. модуля, поддерживающего единый интерфейс вызовов для различных реализаций. Однако одно дело подать идею, а вот реализовать – тут все гораздо сложнее: интерфейс должен быть универсальным и покрывать все возможные требования; необходимо разработать механизм для загрузки нужной реализации интерфейса; требуется каким-то образом связывать интерфейс и реализацию – в итоге нам понадобится сервис поддержки драйверов. Для операционной системы это вполне оправдано, однако для микроконтроллера с его очень ограниченными ресурсами внедрение такого сервиса чревато потерей производительности как из-за большого объема кода, так и из-за дополнительного расхода памяти.
Можно предложить не такое универсальное, зато более простое и менее ресурсоемкое решение с помощью обратных вызовов(Рис. 3). Код опроса упаковывается в отдельный компонент. Перед началом работы происходит настройка, т. е. указанный код как аргумент сохраняется в рабочем коде контроллера. В нужный момент рабочий код делает обратный вызов, выполняет соответствующую функцию и получает требуемое значение. Если необходимо, в процессе работы можно изменять хранимый аргумент, изменяя, таким образом, код опроса датчиков.
Рис. 3. Опрос датчиков с помощью обратного вызова
1.2.2. Вычисления по запросу
Представим, что мы разрабатываем супербыстрый алгоритм сортировки, оптимизированный для работы на нашем многопроцессорном суперкомпьютере. Было потрачено массу усилий, реализовано много кода, и, наконец, алгоритм почти готов. Но вот незадача: мы не знаем заранее, что именно нам нужно сортировать. Сортировка чисел – это самый простой случай, а что делать, если понадобится сортировать, допустим, структуры, содержащие записи из базы данных? Пусть в структуре содержатся сведения о сотрудниках – фамилия, имя, отчество. Как реализовать сортировки по отдельным полям, по совокупности полей? Неужели придется дублировать код для каждого случая?
Простое и эффективное решение указанной проблемы представлено на Рис. 4. Код для сравнения полей упаковывается в отдельный компонент. Когда запускается алгоритм, этот компонент передается как аргумент. В требуемый момент времени алгоритм через указанный аргумент вызовет код сравнения, передавая элементы данных как параметры. Таким образом, можно реализовать различные правила сравнения и передавать их алгоритму без изменения рабочего кода.
Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.
Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.