А. Александреску - Современное проектирование на C++ (1119444), страница 29
Текст из файла (страница 29)
° Обобщенный функтор является обьектом, имеющим ееманпщк> значений, так как он полностью поддерживает копирование, присваивание и передачу параметров по значению. Обобщенный функтор можно свободно копировать, причем он не может содержать виртуальные функции-члены. Обобщенные функторы позволяют хранить вызовы процедур в виде значений, передавать их как параметры и выполнять далеко от места их создания. Они представляют собой усовершенствованный вариант указателей на функции. Существенное различие между указателями на функции и обобщенными функторами заключается в том, что функторы могут хранить состояние объекта и вызывать его функции-члены.
В этой главе изложены следующие сведения. ° Что такое шаблон проектирования Сотввпд и как он связан с обобщенными функтора ми. ° В каких ситуациях они могут принести пользу. ° Внутреннее устройство различных функциональных сущностей в языке С++ и способы их инкапсуляции с помощью единообразного интерфейса. ° Как хранить вызов пропедуры и некоторые или вае ее аргументы внутри объек- та, передавать ее другим объектам и свободно ее вызывать. ° Как связывать между собой несколько отложенных вызовов и последовательно их выполнять. ° Как использовать мощный шаблонный класс рипстог, реализующий опиаан- ные выше функииональные возлюжноати. 5.1. Шаблон Сопппапд В классической книге о шаблонах проектирования (Оапнпа ез а1,, 1995) утверждаетая, что шаблон Соввапд предназначен для инкипсулированип запроса внутри обаскта. Объект этого класса представляет собой часть работы, хранящуюся отдельно от своего исполнителя, Общая структура шаблона соввапд представлена на рис.
5.1. Основной частью шаблона является сам класс Соввапб. Его основное препназначение — ослабить зависимость между двумя частями системы: вызывающим модулем и получателем. Типичная последовательность действий такова. 1. Приложение (клиент) создает объект класса Сопсгетесоввапд, передавая ему информаиию„которой достаточно для выполнения задачи. Пунктирная линия на рис, 5,1 иллюстрирует тот факт, что клиент влияет на состояние объекта класса Сопс гесеСоввапд. 2. Приложение передает интерфейс соввапд объекта класса сопсгетесоввапд вы- зывающему объекту, который сохраняет этот интерфейс.
3. Позднее вызывающий объект выбирает момент для начала действий и активизирует виртуальную функцию-член Ехеаите из класса Соввапд. Механизм виртуальных вызовов переадресует этот вызов объекту класса Сопагетесоввапд, который отслеживает все детали. Объект класса сопсгетесоввапд связывается с объектом класса весе(нег (это одно из его заданий) и использует его для выполнения реальной обработки данных, например, вызывая функиию-член лат)оп. В противном случае объект класса СопсгетеСоввапс) может сам выполнить всю работу. В этом случае объект класса весе(нег на рис.
5.1 не нужен. Рос. 5, й Шаблон просктировонил Соттопо' 12г Часть й. Компоненты Вызывающий объект может свободно вызывать функцию схесцте. Еше более важно то, что в ходе выполнения программы в вызывающий объект можно встроить разные действия, заменив объект класса соввапд, который в нем хранится. Следует отметить два момента. Во-первых, вызывающий модуль не знает, как выполняется работа. Это отнюдь не ново — чтобы использовать алгоритм сортировки, не обязательно знать, как именно он реализован. Однако вызывающий объект не знает даже, для какого вида обработки предназначен класс соввапд.
(Однако алгоритм сортировки обязан выполнять именно сортировку, а не что-то иное.) Вызывающий объект лишь вызывает функцию ехесцте из объекта класса соввапд при наступлении определенного события, С другой стороны, получатель не обязан знать, что его функция-член дстдоп была вызвана каким-то объектом. Таким образом, объект класса соввапд гарантирует изоляцию вызывающего объекта от получателя вызова.
Онн могут быть абсолютно взаимно невилимыми, общаясь лишь через объект класса соввапд. Обычно связи между вызывающим объектом и получателем вызова устанавливает объект класса лрр1тсастоп. Это значит, что для заданного множества получателей можно использовать разные вызывающие модули, причем в заданный вызывающий модуль можно встраивать разных получателей -- и все это происходит в условиях полной изоляции этих объектов.
Во-вторых, посмотрите на шаблон соввапд в перспективе. В обычных программистских задачах, когда нужно выполнить какое-то действие, при вызове задаются объект, его функция-член и ее аргументы. пэпдоп.яез(ге(0, О, 200, 100); // изменение размеров окна Момент инициализации такого вызова в принципе невозможно отличить от момента сбора его элементов в одно целое (объекта, процедуры и аргументов). Однако в шаблоне Соввапд вызывающий объект уже содержит элементы вызова, откладывая сам вызов на неопределенное время. Это иллюстрируется следующим примером.
соввапд гезтхеСвд( кд пдон, // объект йетпдоп::яез(хе, // еункция-член О, О, 200, 100); // аргументы // позднее гез1аесвд.ехесисео; // изменение размера окна (Ниже мы остановимся на малоизвестной конструкции языка С~-+ йе1пдоп:: яезэхе.) В шаблоне Соввапд момент сбора информации об окружении, необходимой для обработки данных, отличается от момента выполнения собственно абрабагаки, В промежутке между этими двумя моментами программа хранит и передает запрос на обработку в виде объекта.
Если бы не существовала возможность отложить вызов, ие было бы и самого шаблона соввапд. С этой точки зрения само существование объекта класса соввапд обусловлено возможностью откладывания вызова, поскольку запрос в это время нужно где-то хранить. Отсюда следуют два важных аспекта шаблона соввапд. ° Изоляция инаер(йейсав. Вызывающий обьект изолирован от получателя вызова. ° Разделение времени.
Объект класса соввапд хранит готовый к выполнению отложенный запрос. Знание окружения также необходимо. Окруэкение (епч!гопгаегн) точки выполнения — это множество сущностей (переменных и функций), которые являются видимыми из этой точки. В момент начала обработки необходимое окружение должно Глава 5. Обобщенные функторы 123 быть доступным, в противном случае запуск невозможен. Объект класса сопсгетесоаааапд может хранить часть нужной информации о своем окружении в виде своего состояния, а доступ к остальной информации получать во время вызова функции вхесите. Чем больше информации об окружении хранится в объекте класса сопсгетесоввапд, тем более он независим. С точки зрения реализации можно идентифицировать две разновидности классов сопсгетесоапапд.
Некоторые из них просто поручают работу получателю, вызывая функщпочлен объекта класса весе(чег, Такие классы называются командами педесььжи (Гопчагд)пй сопипапдз). Другие классы выполняют более сложную работу. Они также мсяуг вызывать функции-члены других объектов, но имеют более сложную логику функционирования. Такие классы называются акгяивиымя «омаядами (асбче сопипапдз). Такая кчассификация команд позволяет очертить границы обобщенной реализации. Активные команды нельзя описать раз и навсегда — их код по определению зависит от конкретного приложения. В то же время на основе команд пересылки можно создавать шаблонные классы.
Поскольку команды пересылки напоминают указатели на функции и похожи на функторы, мы будем называть их обобщенными фувкторами (йепегайхед Йпсгогз). Оставшаяся часть главы посвящена разработке шаблонного класса гопстог, инкапсулнруюпзего любой объект, любую функцию-член этого объекта и любой набор аргументов, относящихся к этой функции. В момент активизации обобщенный функтор собирает асе компоненты в одно целое, создавая вызов функции. Объект класса Гипстог может оказать неоценимую помощь при проектировании с использованием шаблона Соввапд.
В реализациях, разработанных вручную, шаблон соахаапд масштабируется не слишком хорошо -- приходится писать много маленьких классов Сопвапд (по олному на каждую операцию: Спддддцзег, свдре1етецзег, смдмодзФуцзег и т.д.), каждый из которых содержит тривиальную функцию-член ехесите, просто вызывающую конкретную функцию-член некоторого объекта.