диспетчеризация
Паттерн "Посетитель (Visitor)", реализации на java, количество: 9 - CodeLAB.YOURS # Лаборатория кода # релиз 2.0
каталог |
задачи |
паттерны |
исходники |
стат |
форумы |
ссылки
гость
искать
в
описании задач
описании паттернов
описании исходников решений
коде исходников решений
форуме
Главная
>> Каталог задач
>> Паттерны
>> Поведения
>> Посетитель (Visitor)
<< назад
распечатать
обсудить >>
Посетитель (Visitor)
реализации: java, количество: 9
Aвтор: this
Дата: 10.10.2007
Просмотров: 713
Рейтинг: 6/0,6(1)
реализации(исходники)
+(добавить свой вариант)
Имя
Условия
Мотивация
Признаки применения
Решение
Участники
Схема использования
Реализация
Результаты
Пример
История
"Коллеги"
Имя«Паттерн Visitor» codelab.ru codelab.ru оригинал источник
Посетитель - паттерн поведения объектов, задающий стратегии обхода. оригинал codelab.ru источник codelab.ru
Условия, Задача, НазначениеОписывает операцию, которая должна быть выполнена над каждым объектом из некоторой произвольной структуры.
Позволяет определить диспетчеризация задать на выполнение новую операцию, не изменяя ни классы этих объектов, ни их структуры. codelab.ru источник оригинал codelab.ru
МотивацияРассмотрим компилятор, который представляет программу в виде абстрактного синтаксического дерева. Над такими деревьями он должен выполнять операции «статического семантического» анализа, например, проверять, что все переменные определены. Еще ему нужно генерировать код. Аналогично можно было бы определить операции контроля типов, оптимизации кода, анализа потока выполнения, проверки того, что каждой переменной было присвоено конкретное значение перед первым использованием, диспетчеризация т.д. Более того, абстрактные синтаксические деревья могли бы служить для красивой печати программы, реструктурирования кода диспетчеризация вычисления различных метрик программы. источник codelab.ru оригинал codelab.ru
В большинстве таких операций узлы дерева, представляющие операторы присваивания, следует рассматривать иначе, чем узлы, представляющие переменные диспетчеризация арифметические выражения. Поэтому один класс будет создан для операторов присваивания, другой - для доступа к переменным, третий – для арифметических выражений диспетчеризация т.д. Набор классов узлов, конечно, зависит от компилируемого языка, но не очень сильно. источник codelab.ru codelab.ru оригинал
оригинал codelab.ru codelab.ru источник
На представленной диаграмме показана часть иерархии классов Node. Проблема здесь в том, что если раскидать все требуемые операции по классам различных узлов, то получится система, которую трудно понять, сопровождать диспетчеризация изменять. Вряд ли кто-нибудь разберется в программе, если код, отвечающий за проверку типов, будет перемешан с кодом, реализующим красивую печать или анализ потока выполнения. Кроме того, добавление любой новой операции потребует перекомпиляции всех классов. Оптимальный вариант - наличие возможности добавлять операции по отдельности диспетчеризация отсутствие зависимости классов узлов от применяемых к ним операций. codelab.ru codelab.ru источник оригинал
И того, диспетчеризация другого можно добиться, если поместить взаимосвязанные операции из каждого класса в отдельный объект, называемый посетителем, диспетчеризация передавать его элементам абстрактного синтаксического дерева по мере обхода. «Принимая» посетителя, элемент посылает ему запрос, в котором содержится, в частности, класс элемента. Кроме того, в запросе присутствует в виде аргумента диспетчеризация сам элемент. Посетителю в данной ситуации предстоит выполнить операцию над элементом, ту самую, которая наверняка находилась бы в классе элемента. codelab.ru оригинал источник codelab.ru
Например, компилятор, который не использует посетителей, мог бы проверить тип процедуры, вызвав операцию TypeCheck для представляющего ее абстрактного синтаксического дерева. Каждый узел дерева должен был реализовать операцию TypeCheck путем рекурсивного вызова ее же для своих компонентов (см. приведенную выше диаграмму классов). Если же компилятор проверяет тип процедуры посредством посетителей, то ему достаточно создать объект класса TypeCheckingVisitor диспетчеризация вызвать для дерева единую для всех узлов операцию Accept, передав ей этот объект в качестве аргумента. Каждый узел должен был реализовать Accept путем обращения к посетителю: узел, соответствующий оператору присваивания, вызывает операцию посетителя VisitAssignment, диспетчеризация узел, ссылающийся на переменную, - операцию VisitVariableReference. To, что раньше было операцией TypeCheck в классе AssignmentNode, стало операцией VisitAssignment в классе TypeCheckingVisitor. codelab.ru источник оригинал codelab.ru
Чтобы посетители могли заниматься не только проверкой типов, нам необходим абстрактный класс NodeVisitor, являющийся родителем для всех посетителей синтаксического дерева. Приложение, которому нужно вычислять метрики программы, определило бы новые подклассы NodeVisitor-а, так что нам не пришлось бы добавлять зависящий от приложения код в классы узлов. Паттерн посетитель инкапсулирует операции, выполняемые на каждой фазе компиляции, в классе Visitor, ассоциированном с этой фазой: оригинал codelab.ru codelab.ru источник
codelab.ru источник codelab.ru оригинал
И вот что происходит на стороне клиента: оригинал источник codelab.ru codelab.ru
источник codelab.ru оригинал codelab.ru
Применяя паттерн посетитель, вы определяете две иерархии классов: одну для элементов, над которыми выполняется операция (иерархия Node), диспетчеризация другую – для посетителей, описывающих те операции, которые выполняются над элементами (иерархия NodeVisitor). Новая операция создается путем добавления подкласса в иерархию классов посетителей. До тех пор пока грамматика языка остается постоянной (то есть не добавляются новые подклассы Node), новую функциональность можно получить путем определения новых подклассов NodeVisitor. оригинал codelab.ru источник codelab.ru
Признаки применения, использования паттерна Посетитель (Visitor)Используйте паттерн посетитель, когда: источник оригинал codelab.ru codelab.ru
В структуре присутствуют объекты многих классовС различными интерфейсами диспетчеризация вы хотите выполнять над ними операции, зависящие от конкретных классов. источник codelab.ru codelab.ru оригинал
Над объектами, входящими в состав структуры, надо выполнять разнообразные, не связанные между собой операции диспетчеризация вы не хотите «засорять» классы такими операциями. Посетитель позволяет объединить родственные операции, поместив их в один класс. Если структура объектов является общей для нескольких приложений, то паттерн посетитель позволит в каждое приложение включить только относящиеся к нему операции. codelab.ru оригинал источник codelab.ru
Классы, устанавливающие структуру объектов, изменяются редко, но новые операции над этой структурой добавляются часто.При изменении классов, представленных в структуре, нужно будет переопределить интерфейсы всех посетителей, диспетчеризация это может вызвать затруднения. Поэтому если классы меняются достаточно часто, то, вероятно, лучше определить операции прямо в них. источник codelab.ru оригинал codelab.ru
Решение оригинал codelab.ru codelab.ru источник
источник codelab.ru оригинал codelab.ru
Участники паттерна Посетитель (Visitor) оригинал источник codelab.ru codelab.ru
Visitor (NodeVisitor) – посетитель.Объявляет операцию VisitXXX для каждого класса ConcreteElement в структуре объектов. Имя диспетчеризация сигнатура этой операции идентифицируют класс, который посылает посетителю запрос Visit-а. Имя метода позволяет посетителю определить, элемент какого конкретного класса он посещает, диспетчеризация ссылка на этот элемент передающая при вызове – обращаться к нему через его интерфейс. codelab.ru codelab.ru оригинал источник
ConcreteVisitor (TypeCheckingVisitor) - конкретный посетитель.Реализует все операции, объявленные в классе Visitor. Каждая операция реализует фрагмент алгоритма, определенного только для класса соответствующего объекта в структуре. Класс ConcreteVisitor предоставляет контекст для этого алгоритма диспетчеризация сохраняет его локальное состояние. Часто в этом состоянии аккумулируются результаты, полученные в процессе обхода структуры. codelab.ru источник оригинал codelab.ru
Element (Node) – элемент.Определяет операцию Accept, которая принимает посетителя в качестве аргумента. оригинал codelab.ru codelab.ru источник
ConcreteElement (AssignmentNode, VariableRefNode) – конкретный элемент.Реализует операцию Accept, принимающую посетителя как аргумент. Реализует он ее каждый раз по-разному: диспетчеризация именно вызывает только тот Visit-метод, который для него предназначен. codelab.ru оригинал codelab.ru источник
ObjectStructure (Program) - структура объектов.Перечисляет свои элементы.Предоставляет посетителю высокоуровневый интерфейс для посещения своих элементов.Может быть как составным объектом (см. паттерн компоновщик), так диспетчеризация коллекцией, например списком или множеством. codelab.ru codelab.ru источник оригинал
Схема использования паттерна Посетитель (Visitor)Клиент, использующий паттерн посетитель, должен создать объект класса ConcreteVisitor, диспетчеризация затем обойти всю структуру, посетив каждый ее элемент. источник codelab.ru оригинал codelab.ru
При посещении элемента ConcreteElement вызывает операцию посетителя, соответствующую своему классу (типу). Элемент передает этой операции себя в качестве аргумента, чтобы посетитель мог при необходимости получить доступ к его состоянию. оригинал источник codelab.ru codelab.ru
На представленной диаграмме взаимодействий показаны отношения между объектом, структурой, посетителем диспетчеризация двумя элементами. codelab.ru оригинал codelab.ru источник
codelab.ru codelab.ru источник оригинал
Вопросы, касающиеся реализации паттерна Посетитель (Visitor)С каждым объектом структуры ассоциирован некий класс посетителя Visitor. источник codelab.ru codelab.ru оригинал
В этом абстрактном классе объявлены операции VisitConcreteElement для каждого конкретного класса ConcreteElement элементов, представленных в структуре. В каждой операции типа Visit аргумент объявлен как принадлежащий одному из классов ConcreteElement, так что посетитель может напрямую обращаться к интерфейсу этого класса. Классы ConcreteVisitor замещают операции Visit с целью реализации поведения посетителя для соответствующего класса ConcreteElement. источник оригинал codelab.ru codelab.ru
В C++ класс Visitor следовало бы объявить приблизительно так:
Visitor [C++]
ссылка
class Visitor {public: virtual void VisitElementA(ElementA*); virtual void VisitElementB(Elements*); // диспетчеризация так далее для остальных конкретных элементовprotected: Visitor() ;};показать в textarea
выделить
class Visitor {
public:
virtual void VisitElementA(ElementA*);
virtual void VisitElementB(Elements*);
// диспетчеризация так далее для остальных конкретных элементов
protected:
Visitor() ;
}; источник codelab.ru оригинал codelab.ru
codelab.ru источник оригинал codelab.ru
Каждый класс ConcreteElement реализует операцию Accept, которая вызывает соответствующую операцию VisitXXX посетителя для своего класса. Следовательно, вызываемая в конечном итоге операция зависит как от класса элемента, так диспетчеризация от класса посетителя. Поэтому можно было бы использовать перегрузку функций, чтобы дать этим операциям одно диспетчеризация то же простое имя, например Visit, диспетчеризация различать только типом передаваемого параметра. Имеются аргументы как за, так диспетчеризация против подобной перегрузки. С одной стороны, подчеркивается, что все операции выполняют однотипный анализ, хотя диспетчеризация с разными аргументами. С другой стороны, при этом читателю программы может быть не вполне понятно, что происходит при вызове. В общем все зависит от того, часто ли вы применяете перегрузку функций диспетчеризация легко ли всегда в ней ориентируетесь. оригинал источник codelab.ru codelab.ru
Конкретные элементы объявляются так:
конкретные элементы обхода [C++]
ссылка
class Element {public: virtual ~Element(); virtual void Accept(Visitors) = 0;protected: Element();}; class ElementA : public Element {public: ElementA(); virtual void Accept(Visitors v) { v.VisitElementA(this); }};class ElementB : public Element {public: ElementB(); virtual void Accept(Visitors v) { v.VisitElementB(this); }};показать в textarea
выделить
class Element {
public:
virtual ~Element();
virtual void Accept(Visitors) = 0;
protected:
Element();
};
class ElementA : public Element {
public:
ElementA();
virtual void Accept(Visitors v) { v.VisitElementA(this); }
};
class ElementB : public Element {
public:
ElementB();
virtual void Accept(Visitors v) { v.VisitElementB(this); }
}; codelab.ru оригинал codelab.ru источник
codelab.ru оригинал codelab.ru источник
Составной класс CompositeElement мог бы реализовать операцию Accept следующим образом:
составной элемент [C++]
ссылка
class CompositeElement : public Element {public: virtual void Accept(Visitors);private: List* _children;};void CompositeElement::Accept (Visitors v) { ListIterator i(_children); for (i.First(); !i.IsDone(); i.Next() { i .Currentltem()->Accept(v) ; } v.VisitCompositeElement(this);}показать в textarea
выделить
class CompositeElement : public Element {
public:
virtual void Accept(Visitors);
private:
List* _children;
};
void CompositeElement::Accept (Visitors v) {
ListIterator i(_children);
for (i.First(); !i.IsDone(); i.Next() {
i .Currentltem()->Accept(v) ;
}
v.VisitCompositeElement(this);
} оригинал codelab.ru codelab.ru источник
оригинал источник codelab.ru codelab.ru
оригинал codelab.ru codelab.ru источник
Переписав для разнообразия все вышеозвученное на Java получим: оригинал codelab.ru codelab.ru источник
Visitor [java]
ссылка
package visitor.ex; public interface Visitor { void VisitElementA(ElementA el); void VisitElementB(ElementB el); void VisitCompositeElement(CompositeElement el);}показать в textarea
выделить
package visitor.ex;
public interface Visitor {
void VisitElementA(ElementA el);
void VisitElementB(ElementB el);
void VisitCompositeElement(CompositeElement el);
} источник codelab.ru codelab.ru оригинал
элементы [java]
ссылка
package visitor.ex; public interface Element { void Accept(Visitor v); } class ElementA implements Element { public void Accept(Visitor v) { v.VisitElementA(this); }} class ElementB implements Element { public void Accept(Visitor v) { v.VisitElementB(this); } } показать в textarea
выделить
package visitor.ex;
public interface Element {
void Accept(Visitor v);
}
class ElementA implements Element {
public void Accept(Visitor v) {
v.VisitElementA(this);
}
}
class ElementB implements Element {
public void Accept(Visitor v) {
v.VisitElementB(this);
}
}
источник codelab.ru codelab.ru оригинал
составной элемент [java]
ссылка
package visitor.ex; import java.util.Iterator;import java.util.List; public class CompositeElement implements Element { private List children; public void Accept(Visitor v) { Iterator childIter = children.iterator(); while (childIter.hasNext()) { childIter.next().Accept(v); } v.VisitCompositeElement(this); } } показать в textarea
выделить
package visitor.ex;
import java.util.Iterator;
import java.util.List;
public class CompositeElement implements Element {
private List children;
public void Accept(Visitor v) {
Iterator childIter = children.iterator();
while (childIter.hasNext()) {
childIter.next().Accept(v);
}
v.VisitCompositeElement(this);
}
}
codelab.ru источник codelab.ru оригинал
оригинал источник codelab.ru codelab.ru
При решении вопроса о применении паттерна посетитель часто возникают два спорных момента: оригинал codelab.ru codelab.ru источник
Двойная диспетчеризация.По своей сути паттерн посетитель позволяет, не изменяя классы, добавлять в них новые операции. Достигает он этого с помощью приема, называемого двойной диспетчеризацией. Данная техника хорошо известна. Некоторые языки программирования (например, CLOS) поддерживают ее явно. Языки же вроде C++ диспетчеризация Smalltalk поддерживают только одинарную диспетчеризацию.Для определения того, какая операция будет выполнять запрос, в языках с одинарной диспетчеризацией неоходимы имя запроса диспетчеризация тип получателя. Например, то, какая операция будет вызвана для обработки запроса GenerateCode, зависит от типа объекта в узле, которому адресован запрос. В C++ вызов GenerateCode для экземпляра VariableRefNode приводит к вызову функции VariableRefNode::GenerateCode (генерирующей код обращения к переменной). Вызов же GenerateCode для узла класса AssignmentNode приводит к вызову функции AssignmentNode::GenerateCode (генерирующей код для оператора присваивания). Таким образом, выполняемая операция определяется одновременно видом запроса диспетчеризация типом получателя.Понятие «двойная диспетчеризация» означает, что выполняемая операция зависит от вида запроса диспетчеризация типов двух получателей. Accept – это операция с двойной диспетчеризацией. Ее семантика зависит от типов двух объектов: Visitor диспетчеризация Element. Двойная диспетчеризация позволяет посетителю запрашивать разные операции для каждого класса элемента (Если есть двойная диспетчеризация, то почему бы не быть тройной, четверной или диспетчеризации произвольной кратности? Двойная диспетчеризация - это просто частный случай множественной диспетчеризации, при которой выбираемая операция зависит от любого числа типов. CLOS как раз диспетчеризация поддерживает множественную диспетчеризацию.В языках с поддержкой двойной или множественной диспетчеризации необходимость в паттерне посетитель возникает гораздо реже.)Поэтому возникает необходимость в паттерне посетитель: выполняемая операция зависит диспетчеризация от типа посетителя, диспетчеризация от типа посещаемого элемента. Вместо статической привязки операций к интерфейсу класса Element мы можем консолидировать эти операции в классе Visitor диспетчеризация использовать Accept для привязки их во время выполнения. Расширение интерфейса класса Element сводится к определению нового подкласса Visitor, диспетчеризация не к модификации многих подклассов Element. codelab.ru оригинал источник codelab.ru
Какой участник несет ответственность за обход структуры.Посетитель должен обойти каждый элемент структуры объектов. Вопрос в том, как туда попасть. Ответственность за обход можно возложить либо на саму структуру объектов, либо на посетителя либо на отдельный объект-итератор (см. паттерн итератор).Чаще всего структура объектов отвечает за обход. Коллекция просто обходит все свои элементы, вызывая для каждого операцию Accept. Составной объект обычно обходит самого себя, «заставляя» операцию Accept посетить потомков текущего элемента диспетчеризация рекурсивно вызвать Accept для каждого из них.Другое решение - воспользоваться итератором для посещения элементов.Можно применить внутренний или внешний итератор, в зависимости от того, что доступно диспетчеризация более эффективно. Поскольку внутренние итераторы реализуются самой структурой объектов, то работа с ними во многом напоминает предыдущее решение, когда за обход отвечает структура.Основное различие заключается в том, что внутренний итератор не приводит к двойной диспетчеризации: он вызывает операцию посетителя с элементом в качестве аргумента, диспетчеризация не операцию элемента с посетителем в качестве аргумента. Однако использовать паттерн посетитель с внутренним итератором легко в том случае, когда операция посетителя вызывает операцию элемента без рекурсии.Можно даже поместить алгоритм обхода в посетитель, хотя закончится это дублированием кода обхода в каждом классе ConcreteVisitor для каждого агрегата ConcreteElement. Основная причина такого решения – необходимость реализовать особо сложную стратегию обхода, зависящую от результатов операций над объектами структуры. оригинал codelab.ru codelab.ru источник
РезультатыНекоторые достоинства диспетчеризация недостатки паттерна посетитель: codelab.ru оригинал codelab.ru источник
Упрощает добавление новых операций.С помощью посетителей легко добавлять операции, зависящие от компонентов сложных объектов. Для определения новой операции над структурой объектов достаточно просто ввести нового посетителя. Напротив, если функциональность распределена по нескольким классам, то для определения новой операции придется изменить каждый класс. codelab.ru источник оригинал codelab.ru
Объединяет родственные операции диспетчеризация отсекает те, которые не имеют к ним отношения.Родственное поведение не разносится по всем классам, присутствующим в структуре объектов, оно локализовано в посетителе. Не связанные друг с другом функции распределяются по отдельным подклассам класса Visitor. Это способствует упрощению как классов, определяющих элементы, так диспетчеризация алгоритмов, инкапсулированных в посетителях. Все относящиеся к алгоритму структуры данных можно скрыть в посетителе. оригинал codelab.ru codelab.ru источник
Добавление новых классов ConcreteElement затруднено.Паттерн посетитель усложняет добавление новых подклассов класса Element. Каждый новый конкретный элемент требует объявления новой абстрактной операции в классе Visitor, которую нужно реализовать в каждом из существующих на данный момент классов ConcreteVisitor. Иногда большинство конкретных посетителей могут унаследовать операцию по умолчанию, предоставляемую классом Visitor, но это, скорее, исключение, чем правило.Поэтому при решении вопроса о том, стоит ли использовать паттерн посетитель, нужно прежде всего посмотреть, что будет изменяться чаще: алгоритм, применяемый к объектам структуры, или классы объектов, составляющих эту структуру. Вполне вероятно, что сопровождать иерархию классов Visitor будет нелегко, если новые классы ConcreteElement добавляются часто. В таких случаях проще определить операции прямо в классах, представленных в структуре. Если же иерархия классов Element стабильна, но постоянно расширяется набор операций или модифицируются алгоритмы, то паттерн посетитель поможет лучше управлять такими изменениями. источник оригинал codelab.ru codelab.ru
Посещение различных иерархий классов.Итератор может посещать объекты структуры по мере ее обхода, вызывая операции объектов. Но итератор не способен работать со структурами, состоящими из объектов разных типов. Так, интерфейс класса Iterator, может всего лишь получить доступ к объектам типа Item:
итератор для доступа к элементам [C++]
ссылка
template class Iterator { // ... Item CurrentltemO const;};показать в textarea
выделить
template
class Iterator {
// ...
Item CurrentltemO const;
};Отсюда следует, что все элементы, которые итератор может посетить, должны иметь общий родительский класс Item.У посетителя таких ограничений нет. Ему разрешено посещать объекты, не имеющие общего родительского класса. В интерфейс класса Visitor можно добавить операции для объектов любого типа. Например, в следующем объявлении:
наличие операций любого типа [C++]
ссылка
class Visitor {public: // ... void VisitMyType(MyType*); void VisitYourType(YourType*);};показать в textarea
выделить
class Visitor {
public:
// ...
void VisitMyType(MyType*);
void VisitYourType(YourType*);
};классы МуТуре диспетчеризация YourType необязательно должны быть связаны отношением наследования. codelab.ru источник codelab.ru оригинал
Аккумулирование состояния.Посетители могут аккумулировать информацию о состоянии при посещении объектов структуры. Если не использовать этот паттерн, состояние придется передавать в виде дополнительных аргументов операций, выполняющих обход, или хранить в глобальных переменных. codelab.ru источник оригинал codelab.ru
Нарушение инкапсуляции.Применение посетителей подразумевает, что у класса ConcreteElement достаточно развитый интерфейс для того, чтобы посетители могли справиться со своей работой. Поэтому при использовании данного паттерна приходится предоставлять много открытых операций для доступа к внутреннему состоянию элементов, что ставит под угрозу инкапсуляцию. codelab.ru codelab.ru оригинал источник
ПримерРассмотрим пример автоматизации ассортимента товаров в магазине компьютерных комплектующих. codelab.ru источник оригинал codelab.ru
Поскольку посетители обычно ассоциируются с составными объектами, то для иллюстрации паттерна посетитель мы воспользуемся классами Equipment/CompositeEquipment, спроектированные в соответствии с паттерном компоновщиком. Для определения операций, создающих инвентарную опись материалов диспетчеризация вычисляющих полную стоимость агрегата, нам понадобится диспетчеризация паттерн посетитель. источник codelab.ru codelab.ru оригинал
Для поддержки посетителя в класс Equipment добавляется операция Accept: Equipment. codelab.ru источник codelab.ru оригинал
Класс составного оборудования, которое включает в себя любое количество внутренних частей (объектов класса Equipment) будет иметь вид: CompositeEquipment. оригинал источник codelab.ru codelab.ru
источник codelab.ru codelab.ru оригинал
Операции класса Equipment возвращают такие атрибуты единицы оборудования, как потребляемую мощность, стоимость диспетчеризация другие. В подклассах эти операции переопределены в соответствии с конкретными типами оборудования (системники, дисководы, электронные платы). источник codelab.ru codelab.ru оригинал
Определим интерфейс посетителя каждого товара нашего магазина: EquipmentVisitor. источник codelab.ru оригинал codelab.ru
источник codelab.ru оригинал codelab.ru
Далее реализуем некоторые конкретные классы товаров (подклассы класса Equipment) нашего магазина. Все они определяют операцию Accept практически одинаково. Она вызывает операцию EquipmentVisitor-а, соответствующую тому классу, который получил запрос Accept: Card, FloppyDisk. codelab.ru codelab.ru источник оригинал
codelab.ru оригинал источник codelab.ru
Виды оборудования, которые содержат другое оборудование (в частности, подклассы CompositeEquipment в терминологии паттерна компоновщик), реализуют Accept путем обхода своих потомков диспетчеризация вызова Accept для каждого из них. codelab.ru codelab.ru источник оригинал
Затем, как обычно, вызывается операция Visit. Например, в случае системного блока Chassis получим: Chassis. codelab.ru оригинал источник codelab.ru
источник codelab.ru оригинал codelab.ru
Подклассы EquipmentVisitor определяют конкретные алгоритмы, применяемые к структуре оборудования. Так, PricingVisitor вычисляет стоимость всего ассортимента, для чего суммирует нетто-цены простых компонентов (например, гибкие диски) диспетчеризация цену со скидкой составных компонентов (например, системные блоки): PricingVisitor. codelab.ru оригинал источник codelab.ru
codelab.ru codelab.ru источник оригинал
Таким образом, посетитель PricingVisitor подсчитает полную стоимость всех узлов конструкции. Заметим, что PricingVisitor выбирает стратегию вычисления цены в зависимости от класса оборудования, для чего вызывает соответствующую функцию-член. Особенно важно то, что для оценки конструкции можно выбрать другую стратегию, просто поменяв класс PricingVisitor. оригинал источник codelab.ru codelab.ru
Посетитель InventoryVisitor подсчитывает итоговое количество каждого вида оборудования во всей конструкции: InventoryVisitor. codelab.ru codelab.ru оригинал источник
источник codelab.ru codelab.ru оригинал
И вот как все это вместе работает: Client. источник оригинал codelab.ru codelab.ru
codelab.ru оригинал источник codelab.ru
Т.е. заполняем ассортимент товаров, диспетчеризация применяем имеющихся посетителей: для подсчета полной стоимости всех товаров магазина, диспетчеризация для их описи. codelab.ru оригинал источник codelab.ru
оригинал источник codelab.ru codelab.ru
Известные применения паттерна Посетитель (Visitor)В компиляторе Smalltalk-80 имеется класс посетителя, который называется ProgramNodeEnumerator. В основном он применяется в алгоритмах анализа исходного текста программы диспетчеризация не используется ни для генерации кода, ни для красивой печати, хотя мог бы. codelab.ru источник codelab.ru оригинал
IRIS Inventor - это библиотека для разработки приложений трехмерной графики. Библиотека представляет собой трехмерную сцену в виде иерархии узлов, каждый из которых соответствует либо геометрическому объекту, либо его атрибуту. Для операций типа изображения сцены или обработки события ввода необходимо по-разному обходить эту иерархию. В Inventor для этого служат посетители, которые называются действиями (actions). Есть различные посетители для изображения, обработки событий, поиска, сохранения диспетчеризация определения ограничивающих прямоугольников. codelab.ru codelab.ru оригинал источник
Чтобы упростить добавление новых узлов, в библиотеке Inventor реализована схема двойной диспетчеризации на C++. Для этого служит информация о типе, доступная во время выполнения, диспетчеризация двумерная таблица, строки которой представляют посетителей, диспетчеризация колонки - классы узлов. В каждой ячейке хранится указатель на функцию, связанную с парой посетитель-класс узла. codelab.ru оригинал источник codelab.ru
Марк Линтон (Mark Linton) ввел термин «посетитель» (Visitor) в спецификацию библиотеки для построения приложений X Consortium's Fresco Application Toolkit. codelab.ru codelab.ru источник оригинал
Родственные паттерныКомпоновщик: посетители могут использоваться для выполнения операции над всеми объектами структуры, определенной с помощью паттерна компоновщик. оригинал codelab.ru источник codelab.ru
Интерпретатор: посетитель может использоваться для выполнения интерпретации. codelab.ru codelab.ru оригинал источник
Реализации:
java(9) +(добавить свой вариант)
1)
Equipment.java, code #529[автор:this]
обсудить >>
Equipment - базовая концепция элемент оборудования, сущность Element, java, code #529 ссылка
Рейтинг:
7/3,5.02(56)
package visitor; import java.util.Iterator; public abstract class Equipment { private String name; private int power; private int netPrice; private int disountPrice; public Equipment(String name) { super(); this.name = name; } public abstract void Accept(EquipmentVisitor v); public String getName() { return name; } public int getDisountPrice() { return disountPrice; } public int getNetPrice() { return netPrice; } public int getPower() { return power; } public void setDisountPrice(int disountPrice) { this.disountPrice = disountPrice; } public void setNetPrice(int netPrice) { this.netPrice = netPrice; } public void setPower(int power) { this.power = power; } /** * Методы составного оборудования. * По-умалчанию - они ничего не делают. */ public void Add(Equipment eq) {} public void Remove(Equipment eq) {} public Iterator getIterator() { return null; }}Сущность Element
Базовый класс конкретных элементов оборудования.Тестировалось на: java 1.5.0_04
обсудить >>
показать в textarea
выделить
package visitor;
import java.util.Iterator;
public abstract class Equipment {
private String name;
private int power;
private int netPrice;
private int disountPrice;
public Equipment(String name) {
super();
this.name = name;
}
public abstract void Accept(EquipmentVisitor v);
public String getName() {
return name;
}
public int getDisountPrice() {
return disountPrice;
}
public int getNetPrice() {
return netPrice;
}
public int getPower() {
return power;
}
public void setDisountPrice(int disountPrice) {
this.disountPrice = disountPrice;
}
public void setNetPrice(int netPrice) {
this.netPrice = netPrice;
}
public void setPower(int power) {
this.power = power;
}
/**
* Методы составного оборудования.
* По-умалчанию - они ничего не делают.
*/
public void Add(Equipment eq) {}
public void Remove(Equipment eq) {}
public Iterator getIterator() { return null; }
}
2)
CompositeEquipment.java, code #530[автор:this]
обсудить >>
CompositeEquipment - составной элемент оборудования, сущность Element, java, code #530 ссылка
Рейтинг:
5/3,4.96(48)
package visitor; import java.util.ArrayList;import java.util.Iterator;import java.util.List; public class CompositeEquipment extends Equipment { private List innerParts = new ArrayList(); public CompositeEquipment(String name) { super(name); } @Override public void Accept(EquipmentVisitor v) { } @Override public int getDisountPrice() { int price = 0; for (Equipment eq : innerParts) { price += eq.getDisountPrice(); } return price; } @Override public int getNetPrice() { int price = 0; for (Equipment eq : innerParts) { price += eq.getNetPrice(); } return price; } @Override public int getPower() { int power = 0; for (Equipment eq : innerParts) { power += eq.getPower(); } return power; } @Override public void Add(Equipment eq) { innerParts.add(eq); } @Override public Iterator getIterator() { return innerParts.iterator(); } @Override public void Remove(Equipment eq) { innerParts.remove(eq); } }Сущность Element
Поддержка составных частей оборудования.Тестировалось на: java 1.5.0_04
обсудить >>
показать в textarea
выделить
package visitor;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class CompositeEquipment extends Equipment {
private List innerParts = new ArrayList();
public CompositeEquipment(String name) {
super(name);
}
@Override
public void Accept(EquipmentVisitor v) {
}
@Override
public int getDisountPrice() {
int price = 0;
for (Equipment eq : innerParts) {
price += eq.getDisountPrice();
}
return price;
}
@Override
public int getNetPrice() {
int price = 0;
for (Equipment eq : innerParts) {
price += eq.getNetPrice();
}
return price;
}
@Override
public int getPower() {
int power = 0;
for (Equipment eq : innerParts) {
power += eq.getPower();
}
return power;
}
@Override
public void Add(Equipment eq) {
innerParts.add(eq);
}
@Override
public Iterator getIterator() {
return innerParts.iterator();
}
@Override
public void Remove(Equipment eq) {
innerParts.remove(eq);
}
}
3)
EquipmentVisitor.java, code #531[автор:this]
обсудить >>
EquipmentVisitor - посетитель списка элементов оборудования, сущность Visitor, java, code #531 ссылка
Рейтинг:
3/7,4.72(43)
package visitor; public interface EquipmentVisitor { void VisitFloppyDisk(FloppyDisk d); void VisitCard(Card c); void VisitChassis(Chassis ch); /* ... * И так далее для всех конкретных подклассов * Equipment */}Сущность Visitor
Интерфейс посетителя элементов оборудования.Тестировалось на: java 1.5.0_04
обсудить >>
показать в textarea
выделить
package visitor;
public interface EquipmentVisitor {
void VisitFloppyDisk(FloppyDisk d);
void VisitCard(Card c);
void VisitChassis(Chassis ch);
/* ...
* И так далее для всех конкретных подклассов
* Equipment
*/
}
4)
Card.java, code #532[автор:this]
обсудить >>
Card - конкретный товар - карта памяти, сущность ConcreteElement, java, code #532 ссылка
Рейтинг:
7/5,5.2(46)
package visitor; public class Card extends Equipment { public Card(String name) { super(name); } @Override public void Accept(EquipmentVisitor v) { v.VisitCard(this); }}Сущность ConcreteElement
Абстракция карты памяти, конкретный вид товара.Тестировалось на: java 1.5.0_04
обсудить >>
показать в textarea
выделить
package visitor;
public class Card extends Equipment {
public Card(String name) {
super(name);
}
@Override
public void Accept(EquipmentVisitor v) {
v.VisitCard(this);
}
}
5)
FloppyDisk.java, code #533[автор:this]
обсудить >>
FloppyDisk - конкретный товар - дисковод, сущность ConcreteElement, java, code #533 ссылка
Рейтинг:
6/3,5(49)
package visitor; public class FloppyDisk extends Equipment { public FloppyDisk(String name) { super(name); } @Override public void Accept(EquipmentVisitor v) { v.VisitFloppyDisk(this); } public int GetDiscountPrice() { // TODO Auto-generated method stub return 0; } public int GetNetPrice() { // TODO Auto-generated method stub return 0; } public int GetPower() { // TODO Auto-generated method stub return 0; } }Сущность ConcreteElement
Конкретный вид товара, дисковод.Тестировалось на: java 1.5.0_04
обсудить >>
показать в textarea
выделить
package visitor;
public class FloppyDisk extends Equipment {
public FloppyDisk(String name) {
super(name);
}
@Override
public void Accept(EquipmentVisitor v) {
v.VisitFloppyDisk(this);
}
public int GetDiscountPrice() {
// TODO Auto-generated method stub
return 0;
}
public int GetNetPrice() {
// TODO Auto-generated method stub
return 0;
}
public int GetPower() {
// TODO Auto-generated method stub
return 0;
}
}
6)
Chassis.java, code #534[автор:this]
обсудить >>
Chassis - конкретный товар - системный блок, сущность ConcreteElement, java, code #534 ссылка
Рейтинг:
3/6,4.91(45)
package visitor; import java.util.Iterator; /** * Оборудование для компьютера * Системный блок */public class Chassis extends CompositeEquipment { public Chassis(String name) { super(name); } @Override public void Accept(EquipmentVisitor v) { v.VisitChassis(this); Iterator parts = getIterator(); while (parts.hasNext()) { parts.next().Accept(v); } }}Сущность ConcreteElement
Системный блок: пример составного элемента.Тестировалось на: java 1.5.0_04
обсудить >>
показать в textarea
выделить
package visitor;
import java.util.Iterator;
/**
* Оборудование для компьютера
* Системный блок
*/
public class Chassis extends CompositeEquipment {
public Chassis(String name) {
super(name);
}
@Override
public void Accept(EquipmentVisitor v) {
v.VisitChassis(this);
Iterator parts = getIterator();
while (parts.hasNext()) {
parts.next().Accept(v);
}
}
}
7)
PricingVisitor.java, code #535[автор:this]
обсудить >>
PricingVisitor - посетитель списка товаров, рассчитывающий суммарную цену, сущность ConcreteVisitor, java, code #535 ссылка
Рейтинг:
6/3,4.98(46)
package visitor; /** * Посетитель рассчитывающий стоимость */public class PricingVisitor implements EquipmentVisitor { /** * Полная цена всего */ private int total; /** * Прибавляем полную стоимость карты * @see visitor.EquipmentVisitor#VisitCard(visitor.Card) */ public void VisitCard(Card c) { total += c.getNetPrice(); } /** * Здесь же, поскольку это системник, содержащий * большое количество внутренних частей - применяем скидку * @see visitor.EquipmentVisitor#VisitChassis(visitor.Chassis) */ public void VisitChassis(Chassis ch) { total += ch.getDisountPrice(); } /** * Прибавляем полную стоимость флоппика * @see visitor.EquipmentVisitor#VisitFloppyDisk(FloppyDisk) */ public void VisitFloppyDisk(FloppyDisk d) { total += d.getNetPrice(); } public int getTotal() { return total; } }Сущность ConcreteVisitor
Конкретный вид посетителя, рассчитывающего суммарную стоимость.Тестировалось на: java 1.5.0_04
обсудить >>
показать в textarea
выделить
package visitor;
/**
* Посетитель рассчитывающий стоимость
*/
public class PricingVisitor implements EquipmentVisitor {
/**
* Полная цена всего
*/
private int total;
/**
* Прибавляем полную стоимость карты
* @see visitor.EquipmentVisitor#VisitCard(visitor.Card)
*/
public void VisitCard(Card c) {
total += c.getNetPrice();
}
/**
* Здесь же, поскольку это системник, содержащий
* большое количество внутренних частей - применяем скидку
* @see visitor.EquipmentVisitor#VisitChassis(visitor.Chassis)
*/
public void VisitChassis(Chassis ch) {
total += ch.getDisountPrice();
}
/**
* Прибавляем полную стоимость флоппика
* @see visitor.EquipmentVisitor#VisitFloppyDisk(FloppyDisk)
*/
public void VisitFloppyDisk(FloppyDisk d) {
total += d.getNetPrice();
}
public int getTotal() {
return total;
}
}
8)
InventoryVisitor.java, code #536[автор:this]
обсудить >>
InventoryVisitor - посетитель списка товаров, рассчитывающий итоговое количество, сущность ConcreteVisitor, java, code #536 ссылка
Рейтинг:
3/4,4.71(41)
package visitor; /** * Посетитель, подсчитывающий количество * каждого вида (опись оборудования) */public class InventoryVisitor implements EquipmentVisitor { private int cardNum, chassisNum, floppyNum; public void VisitCard(Card c) { cardNum++; } public void VisitChassis(Chassis ch) { chassisNum++; } public void VisitFloppyDisk(FloppyDisk d) { floppyNum++; } public int getCardNum() { return cardNum; } public int getChassisNum() { return chassisNum; } public int getFloppyNum() { return floppyNum; } }Сущность ConcreteVisitor
Конкретный вид посетителя, рассчитывающего итоговое количество каждого вида оборудования во всей конструкции.Тестировалось на: java 1.5.0_04
обсудить >>
показать в textarea
выделить
package visitor;
/**
* Посетитель, подсчитывающий количество
* каждого вида (опись оборудования)
*/
public class InventoryVisitor implements EquipmentVisitor {
private int cardNum, chassisNum, floppyNum;
public void VisitCard(Card c) {
cardNum++;
}
public void VisitChassis(Chassis ch) {
chassisNum++;
}
public void VisitFloppyDisk(FloppyDisk d) {
floppyNum++;
}
public int getCardNum() {
return cardNum;
}
public int getChassisNum() {
return chassisNum;
}
public int getFloppyNum() {
return floppyNum;
}
}
9)
Client.java, code #537[автор:this]
обсудить >>
Client - использование посетителей для расчета суммарной стоимости диспетчеризация количества товара каждого вида, java, code #537 ссылка
Рейтинг:
5/7,5.13(40)
package visitor; import java.util.ArrayList;import java.util.Iterator;import java.util.List; public class Client { static List shop = new ArrayList(); /** * @param args */ public static void main(String[] args) { FloppyDisk fdisk1 = new FloppyDisk("3.5"); fdisk1.setDisountPrice(8); fdisk1.setNetPrice(10); shop.add(fdisk1); Card videoCard1 = new Card("GeForce1"); videoCard1.setDisountPrice(80); videoCard1.setNetPrice(100); shop.add(videoCard1); Card videoCard2 = new Card("GeForce2"); videoCard2.setDisountPrice(100); videoCard2.setNetPrice(120); shop.add(videoCard2); Card motherCard1 = new Card("Asus"); motherCard1.setDisountPrice(200); motherCard1.setNetPrice(250); shop.add(motherCard1); Chassis sys1 = new Chassis("AMD"); FloppyDisk fdisk2 = new FloppyDisk("3.5"); fdisk1.setDisountPrice(9); fdisk1.setNetPrice(11); sys1.Add(fdisk2); Card videoCard3 = new Card("GeForce MX"); videoCard3.setDisountPrice(50); videoCard3.setNetPrice(55); sys1.Add(videoCard3); Card motherCard2 = new Card("Gigabyte"); motherCard2.setDisountPrice(200); motherCard2.setNetPrice(250); sys1.Add(motherCard2); shop.add(sys1); /* ... диспетчеризация т.д. другие товары... */ PricingVisitor priceVisitor = new PricingVisitor(); VisitShop(priceVisitor); System.out.println("Полная стоимость товаров магазина: " + priceVisitor.getTotal()); InventoryVisitor countVisitor = new InventoryVisitor(); VisitShop(countVisitor); System.out.println("На данный момент магазин располагает:"); System.out.println("Флопиков - " + countVisitor.getFloppyNum() + " штук,"); System.out.println("Видео диспетчеризация материнских плат - " + countVisitor.getCardNum() + " штук,"); System.out.println("Системных блоков - " + countVisitor.getChassisNum() + " штук"); } public static void VisitShop(EquipmentVisitor v) { Iterator assortment = shop.iterator(); while (assortment.hasNext()) { assortment.next().Accept(v); } } }Реализация, использующая 2 вида посетителей: для расчета суммарной стоимости диспетчеризация количества товара каждого вида.Тестировалось на: java 1.5.0_04
обсудить >>
показать в textarea
выделить
package visitor;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Client {
static List shop = new ArrayList();
/**
* @param args
*/
public static void main(String[] args) {
FloppyDisk fdisk1 = new FloppyDisk("3.5");
fdisk1.setDisountPrice(8);
fdisk1.setNetPrice(10);
shop.add(fdisk1);
Card videoCard1 = new Card("GeForce1");
videoCard1.setDisountPrice(80);
videoCard1.setNetPrice(100);
shop.add(videoCard1);
Card videoCard2 = new Card("GeForce2");
videoCard2.setDisountPrice(100);
videoCard2.setNetPrice(120);
shop.add(videoCard2);
Card motherCard1 = new Card("Asus");
motherCard1.setDisountPrice(200);
motherCard1.setNetPrice(250);
shop.add(motherCard1);
Chassis sys1 = new Chassis("AMD");
FloppyDisk fdisk2 = new FloppyDisk("3.5");
fdisk1.setDisountPrice(9);
fdisk1.setNetPrice(11);
sys1.Add(fdisk2);
Card videoCard3 = new Card("GeForce MX");
videoCard3.setDisountPrice(50);
videoCard3.setNetPrice(55);
sys1.Add(videoCard3);
Card motherCard2 = new Card("Gigabyte");
motherCard2.setDisountPrice(200);
motherCard2.setNetPrice(250);
sys1.Add(motherCard2);
shop.add(sys1);
/* ... диспетчеризация т.д. другие товары... */
PricingVisitor priceVisitor = new PricingVisitor();
VisitShop(priceVisitor);
System.out.println("Полная стоимость товаров магазина: " +
priceVisitor.getTotal());
InventoryVisitor countVisitor = new InventoryVisitor();
VisitShop(countVisitor);
System.out.println("На данный момент магазин располагает:");
System.out.println("Флопиков - " +
countVisitor.getFloppyNum() + " штук,");
System.out.println("Видео диспетчеризация материнских плат - " +
countVisitor.getCardNum() + " штук,");
System.out.println("Системных блоков - " +
countVisitor.getChassisNum() + " штук");
}
public static void VisitShop(EquipmentVisitor v) {
Iterator assortment = shop.iterator();
while (assortment.hasNext()) {
assortment.next().Accept(v);
}
}
}
<< назад наверх
распечатать
обсудить >>
Логин:
Пароль:
напомнить пароль
регистрация
Свежие исходники:
Объекты диспетчеризация Наследование19 дек'07
Синус, приблизительный расчет по таблице19 дек'07
Equipment - базовая концепция элемент...10 окт'07
надежная проверка - установлен ли MS...19 дек'07
Client - использование посетителей для...10 окт'07
Определение сборки. Как узнать какая...2 мар'08
SortByDateStrategy - стратегия...7 окт'07
Интересные задачи:
#qForms, библиотека типичного функционала валидации/построения/связки html-форм. (rate: 7/3)
#Добавление истории операций(undo&redo) в компонент. (rate: 7/4)
#Курсы валют. (rate: 5/3)
#Относительный путь к файлу. (rate: 7/5)
#Создание простейшей таблицы. (rate: 7/6)
#Часики на js. (rate: 7/5)
#Рисование тора. (rate: 5/7)
#Рисование множества Мандельброта. (rate: 7/5)
#Летающие, крутящиеся шарики. (rate: 7/3)
#Вычисление среднего, среднего отклонения, среднеквадратического отклонения диспетчеризация дисперсии заданной выборки. (rate: 7/5)
Популярные задачи:
#Простой генератор случайных чисел. (5695 hits)
#Динамическое формирование выпадающего списка. (2864 hits)
#Постраничный вывод. (2664 hits)
#Масштабирование, пропорциональное изменение размеров картинки. (2500 hits)
#Передача данных из основного во всплывающее-popup окно через POST. (3319 hits)
#Валидация, динамическая проверка заполнения html форм. (3401 hits)
#Циклический сдвиг одномерного массива или строки, 3 уникальных алгоритма. (3372 hits)
#Вездесущий двоичный поиск.... (4556 hits)
#Пирамедальная сортировка. (2188 hits)
#Вычисление среднего, среднего отклонения, среднеквадратического отклонения диспетчеризация дисперсии заданной выборки. (3381 hits)
показать все >>
каталог |
задачи |
паттерны |
исходники |
стат |
форумы |
карта сайта |
контакты |
ссылки
© 2000-2008 CodeLAB Group
Все права защищены
Страница сгенерирована за 0.095171 секунд
Количество запросов к БД: 15,
gzip: off
разделы
доставка кулеров
скачать длинный нард
наркомания
флеш презентация
купить аудиоплееры
peg perego venezia
купить fifa 2006
светоотражающий краска
слименд лифт
macintosh
гравировальный бур
8800 white gold
пазл
золотник 264-27-00
восстановление файл
узи сделать
zip lock
электрокардиограф
бензопила dolmar
втулка переходный
инвертор
степ-аэробика
купить ниппель
акриловый вставка вкладыш
шампанский заказ
snr roulements
kiev apartments service
выведение бородавка
роль ставень
морозильный витрина
стелаж
диспетчеризация