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
- Visitor: Declara una interfície per a una operació que visita elements de l'estructura d'objectes.
- ConcreteVisitor: Implementa les operacions definides a la interfície Visitor.
- Element: Declara una interfície per a acceptar un visitant.
- ConcreteElement: Implementa l'operació accept() que pren un visitant com a argument.
- 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
- Interfície Visitor: Declara les operacions
visit_circle
ivisit_rectangle
. - Interfície Element (Shape): Declara el mètode
accept
que pren un visitant com a argument. - ConcreteElement (Circle i Rectangle): Implementen el mètode
accept
que crida el mètode corresponent del visitant. - ConcreteVisitor (AreaCalculator): Implementa les operacions
visit_circle
ivisit_rectangle
per calcular l'àrea de cada forma. - 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.
Curs de Patrons de Disseny de Programari
Mòdul 1: Introducció als Patrons de Disseny
- Què són els Patrons de Disseny?
- Història i Origen dels Patrons de Disseny
- Classificació dels Patrons de Disseny
- Avantatges i Desavantatges d'Usar Patrons de Disseny
Mòdul 2: Patrons Creacionals
Mòdul 3: Patrons Estructurals
Mòdul 4: Patrons de Comportament
- Introducció als Patrons de Comportament
- Chain of Responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
Mòdul 5: Aplicació de Patrons de Disseny
- Com Seleccionar el Patró Adequat
- Exemples Pràctics d'Ús de Patrons
- Patrons de Disseny en Projectes Reals
- Refactorització Usant Patrons de Disseny
Mòdul 6: Patrons de Disseny Avançats
- Patrons de Disseny en Arquitectures Modernes
- Patrons de Disseny en Microserveis
- Patrons de Disseny en Sistemes Distribuïts
- Patrons de Disseny en Desenvolupament Àgil