Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 [ 83 ] 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105

3. функция Paragraph::Accept пытается применить оператор dynami c cast<Paragraphvi si tor*> к адресу объекта DocElementvisitor, полученного ею в качестве параметра. Поскольку этот объект имеет динамический тип DocStats, который является открытым наследником классов DocElementvisitor и Paragraphvisitor, выполняется приведение типов. (Вот где происходит телепортация!)

4. Теперь функция Paragraph::Accept должна получить указатель на часть класса Paragraphvisitor, которую унаследовал объект DocStats. Функция Paragraph:: Accept применяет к этому указателю виртуальную функцию Visit-Paragraph.

5. Виртуальный вызов достигает функции DocStats: :visitParagraph. Кроме того, она получает в качестве параметра ссылку на инспектируемый абзац. Инспектирование закончено.

Проверим новую диаграмму зависимостей.

• Определение класса DocElement зависит от класса DocElementvisitor по имени. Зависимость по имени означает, что неполного объявления класса DocElementvisitor вполне достаточно.

• Класс Paragraphvisitor - и вообще все базовые классы XArxVisitor - зависят по имени от классов, которые они инспектируют.

• Реализация функции Paragraph::Accept полностью зависит от класса Paragraphvisitor. Полная зависимость означает, что для компиляции кода необходимо полное определение класса.

• Все конкретные определения классов инспекторов полностью зависят от класса DocElementvisitor и всех базовых инспекторов Ajcxvisitor.

Шаблон Acyclic visitor ликвидирует циклические зависимости, но взамен оставляет программисту еще больше работы. Теперь мы должны поддерживать два параллельных множества классов: инспектируемую иерархию, корнем которой является класс DocElement, и множество инспектирующих классов AArxVisitor, по одному на каждый инспектируемый класс. Работать с двумя параллельными иерархиями классов нежелательно, поскольку для этого требуется строгая дисциплина и внимание.

Сравнивая эффективность "простого" щаблона visitor и шаблона Acyclic visitor, следует заметить, что во втором случае при каждом проходе иерархии возникает одно дополнительное динамическое приведение типов. Время, затрачиваемое на это приведение, может быть постоянным, а может зависеть от количества полиморфных классов, возрастая по логарифмическому или линейному закону. Вид этой зависимости зависит от конкретного компилятора. Если эффективность является важным критерием качества программы, то эти затраты времени могут стать существенными. Таким образом, иногда следует использовать "простой" шаблон visitor и поддерживать циклические связи.

Глядя на эту мрачную картину, следует признать, что шаблон visitor представляет собой противоречивую конструкцию. Это не удивительно, поскольку даже Ральф Гамма (Ralf Gamma) из группы GoF заявлял, что шаблон visitor замыкает десятку самых непопулярных шаблонов (Vlissides, 1999).

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



цию шаблона Visitor, представл5ташую собой полную противоположность сказанному выше: ее легко использовать, расширеть и поддерживать. Как это сделать, мы покажем в следующем разделе.

10.4. Обобщенная реализация шаблона Visitor

Разделим реализацию на две основные части.

• Инспектируемые классы. Это классы, входящие в иерархию, которую мы собираемся инспектировать (добавляя в нее операции).

• Инспектирующие классы. Эти классы определяют и реализуют фактические операции.

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

Сначала мы попробуем реализовать обобщенный шаблон Acyclic visitor, поскольку он обладает лучшими качествами с точки зрения зависимостей между его частями. Позднее мы его усовершенствуем, повысив эффективность. В заключение вернемся к реализации классического шаблона visitor, предложенного фуппой GoF, обладающего более высоким быстродействием за счет потери гибкости.

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

Таблица 10.1. Имена компонентов

Принадлежность

Описание

Basevisi table

Библиотека

Основа всех инспектируемых иерархий

visitable

Библиотека

Смешанный класс, благодаря которому класс в

инспектируемой иерархии становится

инспектируемым

DocElement

Приложение

Основа иерархии, которую мы хотим сделать

инспектируемой

Paragraph,

Приложение

Два конкретных инспектируемых класса.

RasterBitmap

производных от класса DocElement

Accept

Библиотека и

Функция-член, которая вызывается для

приложение

инспектирования иерархии

Basevisitor

Библиотека

Основа инспектирующей иерархии

visitor

Библиотека

Смешанный класс, благодаря которому класс в

инспектирующей иерархии становится

инспектирующим

Statistics

Приложение

Конкретный инспектирующий класс

visit

Библиотека и

Функция-член, которая вызывается

приложение

инспектируемыми элементами и применяется к

инспекторам

Часть 11. Компоненты



Сначала обратим внимание на инспектирующую иерархию. Здесь все довольно просто - мы должны предусмотреть некоторые базовые классы для пользователя, т.е. классы, определяющие операцию Visit для заданного типа. Кроме того, необходимо создать фиктивный класс, используемый оператором динамического приведения типов в соответствии с шаблоном Acyclic visitor.

class Basevisitor {

public:

virtual ~BaseVisitorC) {}

Кода для повторного применения здесь немного, но иногда приходится создавать и такие маленькие классы.

Теперь напишем простой шаблонный класс visitor.

template <class Т> class visitor {

public:

virtual void visitCT&) = 0;

В общем случае функция visitor<T>::visit может возвращать значение, тип которого отличается от типа void. Эта функция может передавать полезный результат через функцию visitable: :Accept. Следовательно, в класс visitor нужно добавить второй шаблонный параметр.

template <class Т, typename R = void>

class visitor

public:

typedef T ReturnType;

virtual ReturnType visitCT&) =0;

Для того чтобы проинспектировать иерархию, мы должны создать класс Concrete-visitor, производный от класса Basevisitor, а количество реализаций класса visitor должно совпадать с количеством инспектируемых классов.

class Somevisitor :

public Basevisitor необходим public visitor<RasterBitmap>, public visitor<Paragraph>

public:

void visit(RasterBitmap*) инспектирует класс RasterBitmap void visit(Paragraph&) инспектирует класс Paragraph

Этот код выглядит простым, ясным и легким в использовании. Его структура четко указывает, какие классы подлежат испектированию. И, что еще лучше, компилятор не позволяет конкретизировать класс Somevisitor, если мы не определили все функции visit. В этом проявляется связь между определением класса Somevisitor и именами инспектируемых им классов (RasterBitmap и Paragraph). Эта зависимость вполне понятна, поскольку класс Somevisitor знает об этих типах и нуждается в специальных операциях для работы с ними.

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



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 [ 83 ] 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105