Анимация
JavaScript
|
Главная Библионтека После уточнения класса Cyclicvisitor с помощью оператора typedef, скажем, классом Myvisitor, нам остается лищь применить макрос DEFlNE CYCLl<: vislTABLECMyvisi tor) в соответствующих Инспектируемых классах. typedef Cyclicvisitor < void, тип возвращаемого значения TYPELlST 3CDocElement, Paragraph, RastereitMap) > Myvisitor; class DocElement { public: virtual void visit(Myvisitor*) =0; class paragraph : public DocElement { public: DEFINE CYCLIC VISITABLE(MyVisitor); Вот и все! Как и в классической реализации щаблона visitor, предложенной группой GoF, нужно лищь быть дисциплинированным. Отличие заключается в том, что теперь вам придется держать в голове намного меньще информации. Для того чтобы сделать иерархию инспектируемой в рамках реализации "простого" щаблона visitor, нужно выполнить следующие действия. • Сделать неполное объявление всех классов, входящих в иерархию. • Написать оператор typedef для класса Cyclicvisitor, конкретизированного типом возвращаемого значения и списком инспектируемых типов. (Назовем этот класс Myvisitor.) • Определить в базовом классе чисто виртуальную функцию visit. • Добавить макрос DEFiNE CYCLlC visiTABLE(Myvisitor) в каждый класс иерархии или реализовать функцию Accept вручную. • Сделать каждый конкретный инспектор наследником класса Myvisitor. • Обновлять конкретизацию щаблонного класса Myvisitor (оператор typedef) каждый раз, когда в иерархию инспектируемых классов добавляется новый класс, и - увы! - компилировать ее снова. По сравнению с реализацией "обычного" щаблона visitor обобщенный подход более понятен. Программисту нужно лищь самостоятельно определить класс Myvisitor. С практической точки зрения лучще начинать с щаблона Acyclic visitor, который легче поддерживать, а на щаблон Visitor следует переходить лищь тогда, когда оптимизация становится действительно необходимой. Обобщенные компоненты позволяют легко экспериментировать - для этого нужно изменить лищь одно объявление. Остальной код остается без изменения. Детали реализации щаблона visitor хранятся в библиотеке. Нужно лищь наладить связь между клиентом и библиотекой, чтобы получить доступ к двум разным реализациям шаблона visitor. 10.6. Отладка вариантов Шаблон Visitor имеет много вариантов и настроек. В этом разделе мы покажем, как его можно применять в собственных приложениях. 10.6.1. Функция-ловушка Эта функция рассматривалась в разделе 10.2. Класс visitor может столкнуться с неизвестным типом, производным от базового класса (например, классом DocElement). В этом случае либо компилятор выдаст сообщение об ощибке, либо во время выполнения профаммы будут произведены какие-то действия, предусмотренные по умолчанию. Посмотрим, как рещается эта проблема в реализациях шаблонов visitor и Асу-clicvisitor с помощью обобщенных компонентов. Для "обычного" щаблона Visitor ситуация проста. Если базовый класс вашей иерархии входит в список типов, передаваемых классу CyclicVisitor, существует возможность реализовать функцию-ловушку. В противном случае возникнет ошибка компиляции. Эти две возможности иллюстрируются следующим кодом. неполные объявления, необходимые шаблону "обычному" visitor class DocElement; Базовый класс class Paragraph; class RasterBitmap; class vectorizedDrawing; typedef CyclicVisitor < void, Тип возвращаемого значения TYPELIST 3(Paragraph, RasterBitmap, VectorizedDrawing) > Strictvisitor; Операции перехвата не предусмотрены. при попытке инспектирования неизвестного типа возникает ошибка компиляции typedef CyclicVisitor < void, Тип возвращаемого значения TYPELlST 4(DocElement,Paragraph, RasterBitmap, Vectori zedDrawi ng) > NonStrictvisitor; объявляет функцию visit(DocElement&), которая будет вызываться при попытке проинспектировать неизвестный тип Все это довольно просто. Теперь рассмотрим функцию-ловушку в обобщенной реализации щаблона Acycliс visitor. Здесь применяется интересный прием. Хотя по существу перехват касается инспектирования неизвестного класса известным инспектором, проблема переворачивается: неизвестный инспектор инспектирует известный класс! Вернемся к реализации функции Acceptimpl для шаблона Acyclic visitor. template <typename R = void> class Basevisi table ... как и раньше ... template <class т> static ReturnType Acceptimpl(т& visited, Basevisitor* guest) if (visitor<T>* p = dynamic cast<visitor<T>*>(&guest)) return p->visit(visited); return ReturnTypeO; внимание! Допустим, мы добавляем в иерархию класса DocElement класс vectorizeDrawing, ничего не сообщая об этом конкретным инспекторам. При попьптсе проинспектировать класс VectorizeDrawing динамическое приведение к типу visitor<vectorizeDrawing> завершится аварийно, поскольку классу Visitor ничего не известно о классе VectorizeDrawing. Следовательно, код активирует альтернативную процедуру и вернет значение типа ReturnType, предусмотренное по умолчанию. Именно в этом месте вступает в действие функция-ловущка. Поскольку наща функция Acceptimpl содержит операцию return ReturnTypeO, простора для вариаций не остается. Здесь следует применить стратегию перехвата. template < typename R = void. template <typename, class> class CatchAll = DefaultCatchAll > class Basevisi table { ... как и раньше ... template <class т> static ReturnType Acceptimpl(т& visited, Basevisitor* guest) { if (visitor<T>* p = dynamic cast<visitor<T>*>(&guest)) { return p->visit(visited); Изменение return CatchAll<R, T>::OnUnknownvisitor(visited, guest); Стратегия CanchAll вполне соответствует требованиям щаблона. Она может возвращать значение по умолчанию или код ошибки, генерировать исключительную ситуацию, вызывать виртуальную функцию-член для инспектируемого объекта, пытаться выполнять динамическое приведение типов и т.д. Реализация функции onUn known-visitor значительно зависит от нужд конкретного приложения. В некоторых случаях необходимо, чтобы инспектирование было выполнено для всех типов, входящих в иерархию. В других случаях это относится лишь к некоторым типам, а остальные можно проигнорировать. В реализации шаблона Acyclic Visitor в основном применяется второй подход, поэтому по умолчанию стратегия CatchAll имеет следующий вид. template <class R, class visited> struct DefaultCatchAll static R Onunknownvisi tor(visited*, 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 |