Introducció al Patró Visitor

El patró Visitor és un patró de disseny de comportament que permet definir noves operacions sobre un conjunt d'objectes sense canviar les seves classes. Aquest patró és especialment útil quan es vol afegir funcionalitats a una jerarquia d'objectes complexa sense modificar les classes existents.

Objectius del Patró Visitor

  • Separació de l'algoritme i l'estructura de l'objecte: Permet separar l'algoritme de les estructures de dades sobre les quals opera.
  • Facilitat d'extensió: Facilita l'addició de noves operacions sense modificar les classes dels objectes.
  • Mantenibilitat: Millora la mantenibilitat del codi en separar les operacions de les estructures de dades.

Components del Patró Visitor

  1. Visitor: Declara una interfície per a una operació que visita elements de l'estructura d'objectes.
  2. ConcreteVisitor: Implementa les operacions definides a la interfície Visitor.
  3. Element: Declara una interfície per a acceptar un visitant.
  4. ConcreteElement: Implementa l'operació accept() que pren un visitant com a argument.
  5. ObjectStructure: Pot enumerar els seus elements i permet que un visitant recorri els seus elements.

Diagrama UML

+-----------------+        +-------------------+
|     Visitor     |        |   ConcreteVisitor |
+-----------------+        +-------------------+
| +visitElementA()|        | +visitElementA()  |
| +visitElementB()|<------>| +visitElementB()  |
+-----------------+        +-------------------+
         ^                          ^
         |                          |
+-----------------+        +-------------------+
|     Element     |        |  ConcreteElement  |
+-----------------+        +-------------------+
| +accept()       |        | +accept()         |
+-----------------+        +-------------------+
         ^
         |
+-----------------+
| ObjectStructure |
+-----------------+
| +accept()       |
+-----------------+

Exemple Pràctic

A continuació, es presenta un exemple pràctic del patró Visitor en Python. En aquest exemple, tenim una jerarquia d'objectes que representen diferents tipus de formes geomètriques (Cercle i Rectangle) i un visitant que calcula l'àrea de cada forma.

Codi

from abc import ABC, abstractmethod
import math

# Interfície Visitor
class Visitor(ABC):
    @abstractmethod
    def visit_circle(self, circle):
        pass

    @abstractmethod
    def visit_rectangle(self, rectangle):
        pass

# Interfície Element
class Shape(ABC):
    @abstractmethod
    def accept(self, visitor):
        pass

# ConcreteElement: Circle
class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def accept(self, visitor):
        visitor.visit_circle(self)

# ConcreteElement: Rectangle
class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def accept(self, visitor):
        visitor.visit_rectangle(self)

# ConcreteVisitor: AreaCalculator
class AreaCalculator(Visitor):
    def visit_circle(self, circle):
        area = math.pi * circle.radius ** 2
        print(f"Area of Circle: {area}")

    def visit_rectangle(self, rectangle):
        area = rectangle.width * rectangle.height
        print(f"Area of Rectangle: {area}")

# ObjectStructure
class ShapeCollection:
    def __init__(self):
        self.shapes = []

    def add_shape(self, shape):
        self.shapes.append(shape)

    def accept(self, visitor):
        for shape in self.shapes:
            shape.accept(visitor)

# Exemple d'ús
circle = Circle(5)
rectangle = Rectangle(4, 6)

shapes = ShapeCollection()
shapes.add_shape(circle)
shapes.add_shape(rectangle)

area_calculator = AreaCalculator()
shapes.accept(area_calculator)

Explicació del Codi

  1. Interfície Visitor: Declara les operacions visit_circle i visit_rectangle.
  2. Interfície Element (Shape): Declara el mètode accept que pren un visitant com a argument.
  3. ConcreteElement (Circle i Rectangle): Implementen el mètode accept que crida el mètode corresponent del visitant.
  4. ConcreteVisitor (AreaCalculator): Implementa les operacions visit_circle i visit_rectangle per calcular l'àrea de cada forma.
  5. ObjectStructure (ShapeCollection): Conté una col·lecció de formes i permet que un visitant recorri les formes.

Exercici Pràctic

Exercici

Implementa un nou visitant PerimeterCalculator que calcula el perímetre de les formes Circle i Rectangle.

Solució

class PerimeterCalculator(Visitor):
    def visit_circle(self, circle):
        perimeter = 2 * math.pi * circle.radius
        print(f"Perimeter of Circle: {perimeter}")

    def visit_rectangle(self, rectangle):
        perimeter = 2 * (rectangle.width + rectangle.height)
        print(f"Perimeter of Rectangle: {perimeter}")

# Exemple d'ús
perimeter_calculator = PerimeterCalculator()
shapes.accept(perimeter_calculator)

Errors Comuns i Consells

  • No implementar tots els mètodes de Visitor: Assegura't que tots els mètodes de la interfície Visitor estan implementats en els visitants concrets.
  • Oblidar cridar el mètode accept: Recorda sempre cridar el mètode accept de l'element amb el visitant com a argument.
  • Modificar les classes existents: El patró Visitor permet afegir funcionalitats sense modificar les classes existents. Evita canviar les classes de la jerarquia d'objectes.

Conclusió

El patró Visitor és una eina poderosa per afegir funcionalitats a una jerarquia d'objectes sense modificar les seves classes. Permet separar l'algoritme de les estructures de dades, facilitant l'extensió i la mantenibilitat del codi. Amb la pràctica, es pot aprendre a aplicar aquest patró de manera efectiva en projectes reals.

© Copyright 2024. Tots els drets reservats