
Кодю потиху
   
Профиль
Группа: Участник Клуба
Сообщений: 3684
Регистрация: 23.2.2006
Где: Гомель, Беларусь
Репутация: 47 Всего: 149
|
0000, в данном случае Visitor не подойдет, так как МЕНЯТЬ ОБЪЕКТ НЕЛЬЗЯ СОВСЕМ, а для Посетителя в посещаемом объекте должен быть метод с помощью которого он (посетитель) может начать работу с объектом Вообще, посетители нужны чтобы не захламлять класс кучей операций. Например коллекция может объявить интерфейс для посещения, а разные посетители сортировать его слева направо и справа налево, возводить каждый элемент в определенную степень. Можно даже найти min и max элементы (только вот не помню можно ли в лассическом паттерне Посетитель забрирать нек значения из отработавшего посетителя). По теме вопроса: Red Wind, без наличия у класса интерфейса для перехвата вызова методов определить момент вызова метода из .net нельзя. ИМХО, если бы можно было, то у класса System.Reflection.MethodInfo были бы события типа Calling и Called Цитата(Naum @ 5.2.2007, 10:30 ) | | А то я даже представления не имею, что такое Visitor. |
| Цитата(GoF) | Паттерн Visitor Название и классификация паттерна Посетитель - паттерн поведения объектов. Назначение Описывает операцию, выполняемую с каждым объектом из некоторой струк¬туры. Паттерн посетитель позволяет определить новую операцию, не изменяя классы этих объектов. Мотивация Рассмотрим компилятор, который представляет программу в виде абстракт¬ного синтаксического дерева. Над такими деревьями он должен выполнять опера¬ции «статического семантического» анализа, например проверять, что все пере¬менные определены. Еще ему нужно генерировать код. Аналогично можно было бы определить операции контроля типов, оптимизации кода, анализа потока вы¬полнения, проверки того, что каждой переменной было присвоено конкретное значение перед первым использованием, и т.д. Более того, абстрактные синтакси¬ческие деревья могли бы служить для красивой печати программы, реструктури¬рования кода и вычисления различных метрик программы. В большинстве таких операций узлы дерева, представляющие операторы при¬сваивания, следует рассматривать иначе, чем узлы, представляющие переменные и арифметические выражения. Поэтому один класс будет создан для операторов присваивания, другой - для доступа к переменным, третий - для арифметичес¬ких выражений и т.д. Набор классов узлов, конечно, зависит от компилируемого языка, но не очень сильно. <Тута диаграмма> На представленной диаграмме показана часть иерархии классов Node. Про¬блема здесь в том, что если раскидать все операции по классам различных узлов, то получится система, которую трудно понять, сопровождать и изменять. Вряд ли кто-нибудь разберется в программе, если код, отвечающий за проверку типов, бу¬дет перемешан с кодом, реализующим красивую печать или анализ потока выпол¬нения. Кроме того, добавление любой новой операции потребует перекомпиляции всех классов. Оптимальный вариант - наличие возможности добавлять операции по отдельности и отсутствие зависимости классов узлов от применяемых к ним операций. И того, и другого можно добиться, если поместить взаимосвязанные операции из каждого класса в отдельный объект, называемый посетителем, и передавать его элементам абстрактного синтаксического дерева по мере обхода. «Принимая» посе¬тителя, элемент посылает ему запрос, в котором содержится, в частности, класс эле¬мента. Кроме того, в запросе присутствует в виде аргумента и сам элемент. Посети¬телю в данной ситуации предстоит выполнить операцию над элементом, ту самую, которая наверняка находилась бы в классе элемента. Например, компилятор, который не использует посетителей, мог бы прове¬рить тип процедуры, вызвав операцию TypeCheck для представляющего ее аб¬страктного синтаксического дерева. Каждый узел дерева должен был реализовать операцию TypeCheck путем рекурсивного вызова ее же для своих компонентов (см. приведенную выше диаграмму классов). Если же компилятор проверяет тип процедуры посредством посетителей, то ему достаточно создать объект класса TypeCheckingVisitor и вызвать для дерева операцию Accept, передав ей этот объект в качестве аргумента. Каждый узел должен был реализовать Accept путем обращения к посетителю: узел, соответствующий оператору присваивания, вызы¬вает операцию посетителя Visit Assignment, а узел, ссылающийся на перемен¬ную, - операцию VisitVariableRef erence. To, что раньше было операцией TypeCheck в классе AssignmentNode, стало операцией VisitAssignment в классе TypeCheckingVisitor. Чтобы посетители могли заниматься не только проверкой типов, нам необхо¬дим абстрактный класс Nodevisitor, являющийся родителем для всех посетите¬лей синтаксического дерева. Приложение, которому нужно вычислять метрики программы, определило бы новые подклассы Nodevisitor, так что нам не при¬шлось бы добавлять зависящий от приложения код в классы узлов. Паттерн посе¬титель инкапсулирует операции, выполняемые на каждой фазе компиляции, в классе Visitor, ассоциированном с этой фазой. Применяя паттерн посетитель, вы определяете две иерархии классов: одну для элементов, над которыми выполняется операция (иерархия Node), а другую - для посетителей, описывающих те операции, которые выполняются над элементами (иерархия NodeVisitor). Новая операция создается путем добавления подкласса в иерархию классов посетителей. До тех пор пока грамматика языка остается посто¬янной (то есть не добавляются новые подклассы Node), новую функциональность можно получить путем определения новых подклассов NodeVisitor. Применимость Используйте паттерн посетитель, когда: 1) в структуре присутствуют объекты многих классов с различными интерфей¬сами и вы хотите выполнять над ними операции, зависящие от конкретных классов 2) над объектами, входящими в состав структуры, надо выполнять разнообраз¬ные, не связанные между собой операции и вы не хотите «засорять» классы такими операциями. Посетитель позволяет объединить родственные опера¬ции, поместив их в один класс. Если структура объектов является общей для нескольких приложений, то паттерн посетитель позволит в каждое прило¬жение включить только относящиеся к нему операции; 3) классы, устанавливающие структуру объектов, изменяются редко, но новые операции над этой структурой добавляются часто. При изменении классов, представленных в структуре, нужно будет переопределить интерфейсы всех посетителей, а это может вызвать затруднения. Поэтому если классы меняют¬ся достаточно часто, то, вероятно, лучше определить операции прямо в них. Участники a Visitor (NodeVisitor) - посетитель: - объявляет операцию Visit для каждого класса ConcreteElement в структуре объектов. Имя и сигнатура этой операции идентифицируют класс, который посылает посетителю запрос Visit. Это позволяет посе¬ тителю определить, элемент какого конкретного класса он посещает. Вла¬ дея такой информацией, посетитель может обращаться к элементу напря¬ мую через его интерфейс; a ConcreteVisitor (TypeCheckingVisitor) - конкретный посетитель: - реализует все операции, объявленные в классе Visitor. Каждая операция реализует фрагмент алгоритма, определенного для класса соответствующего объекта в структуре. Класс ConcreteVisitor предоставляет контекст для этого алгоритма и сохраняет его локальное состояние. Часто в этом состоя¬нии аккумулируются результаты, полученные в процессе обхода структуры; a Element (Node) - элемент: - определяет операцию Accept, которая принимает посетителя в качестве аргумента; a ConcreteElement (AssignmentNode, VariableRefNode) - конкретный элемент: - реализует операцию Accept, принимающую посетителя как аргумент; a ObjectStructure (Program) - структура объектов: - может перечислить свои элементы;
- может предоставить посетителю высокоуровневый интерфейс для посе¬ щения своих элементов; - может быть как составным объектом (см. паттерн компоновщик), так и коллекцией, например списком или множеством. Отношения а клиент, использующий паттерн посетитель, должен создать объект класса ConcreteVisitor, азатем обойти всю структуру, посетив каждый ее элемент. а при посещении элемента последний вызывает операцию посетителя, соот¬ветствующую своему классу. Элемент передает этой операции себя в каче¬стве аргумента, чтобы посетитель мог при необходимости получить доступ к его состоянию. На представленной диаграмме взаимодействий показаны отношения меж¬ду объектом, структурой, посетителем и двумя элементами.
Результаты Некоторые достоинства и недостатки паттерна посетитель: Q упрощает добавление новых операций. С помощью посетителей легко добавлять операции, зависящие от компонентов сложных объектов. Для определения новой операции над структурой объектов достаточно просто ввести нового по¬сетителя. Напротив, если функциональность распределена по нескольким клас¬сам, то для определения новой операции придется изменить каждый класс; а объединяет родственные операции и отсекает те, которые не имеют к ним отношения. Родственное поведение не разносится по всем классам, присут¬ствующим в структуре объектов, оно локализовано в посетителе. Не связан¬ные друг с другом функции распределяются по отдельным подклассам класса Visitor. Это способствует упрощению как классов, определяющих элементы, так и алгоритмов, инкапсулированных в посетителях. Все относящиеся к алгоритму структуры данных можно скрыть в посетителе; а добавление новых классов ConcreteElement затруднено. Паттерн посетитель усложняет добавление новых подклассов класса Element. Каждый новый конкретный элемент требует объявления новой абстрактной операции в клас¬се Visitor, которую нужно реализовать в каждом из существующих клас¬сов ConcreteVis itor. Иногда большинство конкретных посетителей могут унаследовать операцию по умолчанию, предоставляемую классом Visitor, что скорее исключение, чем правило. Поэтому при решении вопроса о том, стоит ли использовать паттерн посе¬титель, нужно прежде всего посмотреть, что будет изменяться чаще: алго¬ритм, применяемый к объектам структуры, или классы объектов, составля¬ющих эту структуру. Вполне вероятно, что сопровождать иерархию классов Visitor будет нелегко, если новые классы ConcreteElement добавляют¬ся часто. В таких случаях проще определить операции прямо в классах, пред¬ставленных в структуре. Если же иерархия классов Element стабильна, но постоянно расширяется набор операций или модифицируются алгоритмы, то паттерн посетитель поможет лучше управлять такими изменениями; а посещение различных иерархий классов. Итератор (см. описание паттерна итератор) может посещать объекты структуры по мере ее обхода, вызывая операции объектов. Но итератор не способен работать со структурами, состо¬ящими из объектов разных типов. Так, интерфейс класса Iterator, рассмот¬ренный на стр. 255, может всего лишь получить доступ к объектам типа Item: template <class Item> class Iterator { // ... Item CurrentltemO const; }; Отсюда следует, что все элементы, которые итератор может посетить, долж¬ны иметь общий родительский класс Item. У посетителя таких ограничений нет. Ему разрешено посещать объекты, не имеющие общего родительского класса. В интерфейс класса Visitor мож¬но добавить операции для объектов любого типа. Например, в следующем объявлении class Visitor { public: void VisitMyType(MyType*); void VisitYourType(YourType*); классы МуТуре и YourType необязательно должны быть связаны отноше¬нием наследования; а аккумулирование состояния. Посетители могут аккумулировать информа¬цию о состоянии при посещении объектов структуры. Если не использовать этот паттерн, состояние придется передавать в виде дополнительных аргумен¬тов операций, выполняющих обход, или хранить в глобальных переменных; а нарушение инкапсуляции. Применение посетителей подразумевает, что у клас¬са ConcreteElement достаточно развитый интерфейс для того, чтобы посе¬тители могли справиться со своей работой. Поэтому при использовании дан¬ного паттерна приходится предоставлять открытые операции для доступа к внутреннему состоянию элементов, что ставит под угрозу инкапсуляцию.
|
|