Посетитель — это поведенческий паттерн, который позволяет добавить новую операцию для целой иерархии классов, не изменяя код этих классов.
Подробней о том, почему Посетитель нельзя заменить простой перегрузкой методов читайте в статье Посетитель и Double Dispatch .
Сложность:
Популярность:
Применимость: Посетитель нечасто встречается в TypeScript-коде из-за своей сложности и нюансов реализазации.
Концептуальный пример
Этот пример показывает структуру паттерна Посетитель , а именно — из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.
index.ts: Пример структуры паттерна
/**
* Интерфейс Компонента объявляет метод accept, который в качестве аргумента
* может получать любой объект, реализующий интерфейс посетителя.
*/
interface Component {
accept(visitor: Visitor): void;
}
/**
* Каждый Конкретный Компонент должен реализовать метод accept таким образом,
* чтобы он вызывал метод посетителя, соответствующий классу компонента.
*/
class ConcreteComponentA implements Component {
/**
* Обратите внимание, мы вызываем visitConcreteComponentA, что соответствует
* названию текущего класса. Таким образом мы позволяем посетителю узнать, с
* каким классом компонента он работает.
*/
public accept(visitor: Visitor): void {
visitor.visitConcreteComponentA(this);
}
/**
* Конкретные Компоненты могут иметь особые методы, не объявленные в их
* базовом классе или интерфейсе. Посетитель всё же может использовать эти
* методы, поскольку он знает о конкретном классе компонента.
*/
public exclusiveMethodOfConcreteComponentA(): string {
return 'A';
}
}
class ConcreteComponentB implements Component {
/**
* То же самое здесь: visitConcreteComponentB => ConcreteComponentB
*/
public accept(visitor: Visitor): void {
visitor.visitConcreteComponentB(this);
}
public specialMethodOfConcreteComponentB(): string {
return 'B';
}
}
/**
* Интерфейс Посетителя объявляет набор методов посещения, соответствующих
* классам компонентов. Сигнатура метода посещения позволяет посетителю
* определить конкретный класс компонента, с которым он имеет дело.
*/
interface Visitor {
visitConcreteComponentA(element: ConcreteComponentA): void;
visitConcreteComponentB(element: ConcreteComponentB): void;
}
/**
* Конкретные Посетители реализуют несколько версий одного и того же алгоритма,
* которые могут работать со всеми классами конкретных компонентов.
*
* Максимальную выгоду от паттерна Посетитель вы почувствуете, используя его со
* сложной структурой объектов, такой как дерево Компоновщика. В этом случае
* было бы полезно хранить некоторое промежуточное состояние алгоритма при
* выполнении методов посетителя над различными объектами структуры.
*/
class ConcreteVisitor1 implements Visitor {
public visitConcreteComponentA(element: ConcreteComponentA): void {
console.log(`${element.exclusiveMethodOfConcreteComponentA()} + ConcreteVisitor1`);
}
public visitConcreteComponentB(element: ConcreteComponentB): void {
console.log(`${element.specialMethodOfConcreteComponentB()} + ConcreteVisitor1`);
}
}
class ConcreteVisitor2 implements Visitor {
public visitConcreteComponentA(element: ConcreteComponentA): void {
console.log(`${element.exclusiveMethodOfConcreteComponentA()} + ConcreteVisitor2`);
}
public visitConcreteComponentB(element: ConcreteComponentB): void {
console.log(`${element.specialMethodOfConcreteComponentB()} + ConcreteVisitor2`);
}
}
/**
* Клиентский код может выполнять операции посетителя над любым набором
* элементов, не выясняя их конкретных классов. Операция принятия направляет
* вызов к соответствующей операции в объекте посетителя.
*/
function clientCode(components: Component[], visitor: Visitor) {
// ...
for (const component of components) {
component.accept(visitor);
}
// ...
}
const components = [
new ConcreteComponentA(),
new ConcreteComponentB(),
];
console.log('The client code works with all visitors via the base Visitor interface:');
const visitor1 = new ConcreteVisitor1();
clientCode(components, visitor1);
console.log('');
console.log('It allows the same client code to work with different types of visitors:');
const visitor2 = new ConcreteVisitor2();
clientCode(components, visitor2);
Output.txt: Результат выполнения
The client code works with all visitors via the base Visitor interface:
A + ConcreteVisitor1
B + ConcreteVisitor1
It allows the same client code to work with different types of visitors:
A + ConcreteVisitor2
B + ConcreteVisitor2
Посетитель на других языках программирования