|
Программирование >> Многопоточная библиотека с принципом минимализма
Шаблон Visitor в этой главе обсуждаются обобщенные компоненты, использующие щаблон проектирования visitor (Gamma et al., 1995). Это мощный щаблон проектирования, измешпо-щий зависимость между проектными решениями, принятыми при разработке класса. Шаблон visitor (Инспектор) обеспечивает удивительную гибкость: в иерархию классов можно добавлять виртуальные функции, не прибегая к повторной компиляции ни этих функций, ни существующих клиентских кодов. Однако эта гибкость достигается за счет определенных жертв: в иерархию теперь невозможно добавить новый терминальный класс (leaf class), не повторив компиляцию всей иерархии и ее клиентов. Следовательно, область применения шаблона visitor ограничивается только очень устойчивыми иерархиями (в которые редко добавляются новые классы) и программами, в которые часто приходится добавлять новые виртуальные функции. Шаблон Visitor противоречит программистской интуиции. Следовательно, для его успешного применения нужны тщательная реализация и строгая дисциплина. В главе описывается обобщенная реализация шаблона visitor, оставляющая прикладному программисту очень мало работы. В главе обсуждаются следующие темы. Принцип работы шаблона visitor. Когда следует применять шаблон visitor и, что не менее важно, когда его применять не следует. Базовая реализация инспектора (предложенная группой GoF). Как устранить некоторые недостатки базовой реализации шаблона Visitor. Как реализовать решения, касающиеся реализации шаблона Visitor, в виде библиотеки. Мощные обобщенные компоненты, облегчающие реализацию инспекторов. 10.1. Основы шаблона Visitor Рассмотрим иерархию классов, которую мы хотим наделить новыми функциональными возможностями. Для этого в нее можно добавлять либо новые классы, либо новые виртуальные функции-члены. Добавить новые классы легко. Можно применить механизм наследования к терминальному классу и реализовать необходимые виртуальные функции. Для этого не нужно изменять или компилировать вновь существующие классы. Их коды остаются неизменными и готовы к повторному использованию. в противоположность этому, добавить новую виртуальную функцию трудно. Для того чтобы полиморфно манипулировать объектами (с помощью указателей на объекты базового класса), виртуальную функцию-член нужно добавить не только в базовый класс, но и, возможно, во многие другие классы, входящие в иерархию. Это - основная операция. Она модифицирует базовый класс, от которого зависит вся иерархия и ее клиенты. В результате все придется повторно компилировать. Короче говоря, с точки зрения зависимостей между классами новые классы добавлять легко, а новые виртуальные функции-члены - трудно. Однако допустим, что у нас есть иерархия, в которую редко добавляются новые классы и в то же время часто добавляются новые виртуальные функции-члены. В этой ситуации возможность легко добавлять новые классы представляет собой ненужное преимущество. Вот тут-то и пригодится щаблон visitor. Этот щаблон предоставляет профаммисту именно ту функциональную возможность, в которой он в данный момент нуждается, позволяя легко вставлять в иерархию новые виртуальные функции-члены, одновременно затрудняя вставку новых классов. За это придется заплатить всего-навсего лищним виртуальным вызовом. Шаблон Visitor лучще всего проявляет свои возможности, когда операции на объектами не связаны между собой. Парадигма состояние и операции для каждого класса становится не совсем уместной. В данной ситуации лучще придерживаться принципа отделения типов от операций. Имеет смысл хранить разные реализации одной операции вместе, а не распределять их по иерархии классов. Допустим, что мы разрабатываем текстовый редактор. Элементы документа, такие как абзащ.1 и графические изображения, представляются в виде классов, производнь[х от одного базового класса, скажем, DocElement. Документ - это сфуктурированный набор указателей на объекты класса DocElement. Профаммисту нужна возможность перемещаться по этой сфуктуре и выполнять операции, например, проверку орфофафии, переформатирование и вычисление статистических показателей. В идеале следовало бы реализовывать эти операции, добавив новый код без модификации существующей профаммы. Более того, профамму было бы легче сопровождать, если бы весь код, связанный, скажем, с вычислением статистических показателей, хранился в одном месте. К статистическим показателям могут относиться общее количество символов, значащих символов, слов и рисунков. Все это естественным образом можно поместить в класс DocStats. class DocStats { unsigned int, chars , nonBlankChars , words , images ; public: void AddChars(unsigned int charsToAdd) { chars += charsToAdd; } ... классы Addwords, Addlmages и др. определяются так же ... Статистика отображается в диалоговом окне void DisplayО; }; Используя классический объектно-ориентированный подход к сбору статистики, можно определить в классе DocElement соответствующую виртуальную функцию. class DocElement { эта функция-член предназначена для сбора статистики virtual void updateStatsCDocStats& statistics) = 0; В этом случае каждый конкретный элемент документа может определять эту функцию по-своему. Например, классы Paragraph и RasterBitmap, производные от класса DocElement, могут реализовывать функцию updateStats следующим образом. void Paragraph::updateStatsCDocStats& statistics) { statistics.AddCharsC/соугы/ес/яво символов в абзаце) ; statistics.AddwordsC/соугы/ес/яво слов в абзаце); void RasterBitmap::updateStatsCDocStats& statistics) { Этот класс учитывает только рисунки и больше ничего (символы и другие элементы игнорируются) statistics.Addlmages(l); В итоге функция, управляющая сбором статистических данных, может выглядеть так. voi d Document::Di splayStati sti cs() { DocStats statistics; for (каждый элемент документа) 3yzejMeHm->updateStats(statistics); } statistics .DisplayO; Это очень хорощая реализация возможностей по сбору статистики, однако у нее есть ряд недостатков. Она требует, чтобы класс DocElement и производные от него классы имели доступ к определению функции DocStats. Следовательно, при каждой модификации класса DocStats придется компилировать заново всю иерархию класса DocElement. Фактические операции по сбору статистики рассеяны по разным реализациям функции UpdateStats. При отладке или расщирении возможностей, связанных со сбором статистики, придется искать и редактировать несколько файлов. Реализация не масштабируется по мере добавления новых операций, аналогичных сбору статистики. Для того чтобы добавить операцию увеличить размер шрифта на один пункт , понадобится добавить в класс DocElement новую виртуальную функцию, преодолевая все связанные с этим трудности. Однако связь, существующую между классами DocElement и DocStats, можно разорвать, если переместить все операции внутрь класса DocStats и позволить ему самому определять, какую операцию выполнять для того или иного типа. Для этого нужно, чтобы в
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |