knowledge/technology/programming/patterns/behavioral/Visitor Pattern.md
2023-12-04 11:02:23 +01:00

96 lines
No EOL
4.2 KiB
Markdown

---
obj: concept
---
# Visitor Pattern
## **Description:**
The Visitor Pattern is a behavioral design pattern that allows you to add new operations to a set of related classes without modifying their code. It separates the algorithm (or operation) from the object structure, enabling you to define new behaviors (visitors) independently and apply them to existing objects. This pattern is useful when you have a stable set of classes but need to frequently introduce new operations on those classes.
**How it's Used:**
1. **Element Interface**: Define an element interface that declares an `accept` method which accepts a visitor.
2. **Concrete Elements**: Create concrete element classes that implement the element interface. These are the objects that will be visited by visitors.
3. **Visitor Interface**: Define a visitor interface that declares visit methods for each concrete element type. The visitor interface should have one visit method for each type of element it can visit.
4. **Concrete Visitors**: Create concrete visitor classes that implement the visitor interface. Each concrete visitor provides implementations for the visit methods to perform specific operations on the elements.
5. **Element Structure**: Create a structure (e.g., a collection) of concrete elements that need to be visited.
6. **Accept Visitor**: In the concrete elements, implement the `accept` method to call the appropriate visit method on the visitor.
7. **Client Code**: In client code, create instances of concrete elements and visitors, and apply visitors to elements as needed.
## **Example:**
Let's create an example of the Visitor Pattern in [Python](../../languages/Python.md) to model a document structure with elements (e.g., paragraphs and tables) and a visitor that counts the elements.
```python
from abc import ABC, abstractmethod
# Step 1: Element Interface
class Element(ABC):
@abstractmethod
def accept(self, visitor):
pass
# Step 2: Concrete Elements
class Paragraph(Element):
def accept(self, visitor):
visitor.visit_paragraph(self)
class Table(Element):
def accept(self, visitor):
visitor.visit_table(self)
# Step 3: Visitor Interface
class Visitor(ABC):
@abstractmethod
def visit_paragraph(self, paragraph):
pass
@abstractmethod
def visit_table(self, table):
pass
# Step 4: Concrete Visitors
class ElementCounter(Visitor):
def __init__(self):
self.paragraph_count = 0
self.table_count = 0
def visit_paragraph(self, paragraph):
self.paragraph_count += 1
def visit_table(self, table):
self.table_count += 1
# Step 5: Element Structure
class Document:
def __init__(self, elements):
self.elements = elements
def accept(self, visitor):
for element in self.elements:
element.accept(visitor)
# Step 7: Client Code
def main():
paragraph1 = Paragraph()
paragraph2 = Paragraph()
table1 = Table()
table2 = Table()
elements = [paragraph1, table1, paragraph2, table2]
document = Document(elements)
counter = ElementCounter()
document.accept(counter)
print(f"Paragraph Count: {counter.paragraph_count}")
print(f"Table Count: {counter.table_count}")
if __name__ == "__main__":
main()
```
In this [Python](../../languages/Python.md) example, we define an `Element` interface (Step 1) with an `accept` method. We create two concrete element classes (`Paragraph` and `Table`) that implement this interface (Step 2).
We define a `Visitor` interface (Step 3) with `visit_paragraph` and `visit_table` methods. We then create a concrete visitor class (`ElementCounter`) that implements the visitor interface (Step 4) and counts the elements it visits.
The `Document` class (Step 5) contains a collection of elements, and it implements the `accept` method to iterate through the elements and call their `accept` method to let the visitor visit them.
In the `main` function (Step 7), we create instances of elements and visitors, apply the visitor to the document, and display the counts of paragraphs and tables visited. This separation allows us to add new visitors to perform different operations on the document structure without modifying the element classes.