Анимация
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

После уточнения класса 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