Patrón de diseño de visitantes

El patrón de diseño de visitantes es uno de los patrones de diseño de comportamiento. Se utiliza cuando tenemos que realizar una operación sobre un grupo de Objetos de tipo similar. Con la ayuda del patrón de visitante, podemos mover la lógica operativa de los objetos a otra clase.

El patrón de visitante consta de dos partes:

  • un método llamado Visit() que implementa el visitante y se llama para cada elemento en la estructura de datos
  • clases visitables que proporcionan métodos Accept() que aceptan un visitante

Diagrama UML Patrón de diseño de visitantes

Visitor-Design-Pattern-Diagram

Componentes de diseño

  • Cliente: la clase Cliente es un consumidor de las clases del patrón de diseño del visitante. Tiene acceso a los objetos de la estructura de datos y puede indicarles que acepten un Visitante para realizar el procesamiento apropiado.
  • Visitante: Esta es una interfaz o una clase abstracta utilizada para declarar las operaciones de visita para todos los tipos de clases visitables.
  • ConcreteVisitor: Para cada tipo de visitante se deben implementar todos los métodos de visita, declarados en resumen visitante. Cada Visitante será responsable de diferentes operaciones.
  • Visitable: esta es una interfaz que declara la operación de aceptación. Este es el punto de entrada que permite que un objeto sea «visitado» por el objeto visitante.
  • ConcreteVisitable: estas clases implementan la interfaz o clase Visitable y definen la operación de aceptación. El objeto visitante se pasa a este objeto mediante la operación de aceptación.

Veamos un ejemplo de patrón de diseño Visitor en Java.

interface ItemElement
{
    public int accept(ShoppingCartVisitor visitor);
}
  
class Book implements ItemElement
{
    private int price;
    private String isbnNumber;
   
    public Book(int cost, String isbn)
    {
        this.price=cost;
        this.isbnNumber=isbn;
    }
   
    public int getPrice() 
    {
        return price;
    }
   
    public String getIsbnNumber() 
    {
        return isbnNumber;
    }
   
    @Override
    public int accept(ShoppingCartVisitor visitor) 
    {
        return visitor.visit(this);
    }
   
}
  
class Fruit implements ItemElement 
{
    private int pricePerKg;
    private int weight;
    private String name;
   
    public Fruit(int priceKg, int wt, String nm)
    {
        this.pricePerKg=priceKg;
        this.weight=wt;
        this.name = nm;
    }
   
    public int getPricePerKg() 
    {
        return pricePerKg;
    }
   
    public int getWeight() 
    {
        return weight;
    }
   
    public String getName()
    {
        return this.name;
    }
   
    @Override
    public int accept(ShoppingCartVisitor visitor) 
    {
        return visitor.visit(this);
    }
   
}
  
interface ShoppingCartVisitor 
{
   
    int visit(Book book);
    int visit(Fruit fruit);
}
  
class ShoppingCartVisitorImpl implements ShoppingCartVisitor
{
   
    @Override
    public int visit(Book book)
    {
        int cost=0;
        //apply 5$ discount if book price is greater than 50
        if(book.getPrice() > 50)
        {
            cost = book.getPrice()-5;
        }
        else 
            cost = book.getPrice();
              
        System.out.println("Book ISBN::"+book.getIsbnNumber() + " cost ="+cost);
        return cost;
    }
   
    @Override
    public int visit(Fruit fruit) 
    {
        int cost = fruit.getPricePerKg()*fruit.getWeight();
        System.out.println(fruit.getName() + " cost = "+cost);
        return cost;
    }
   
}
  
class ShoppingCartClient 
{
   
    public static void main(String[] args) 
    {
        ItemElement[] items = new ItemElement[]{new Book(20, "1234"),
                              new Book(100, "5678"), new Fruit(10, 2, "Banana"),
                              new Fruit(5, 5, "Apple")};
   
        int total = calculatePrice(items);
        System.out.println("Total Cost = "+total);
    }
   
    private static int calculatePrice(ItemElement[] items) 
    {
        ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
        int sum=0;
        for(ItemElement item : items)
        {
            sum = sum + item.accept(visitor);
        }
        return sum;
    }
   
}
Producción:

Book ISBN::1234 cost =20
Book ISBN::5678 cost =95
Banana cost = 20
Apple cost = 25
Total Cost = 160

Aquí, en la implementación, si el método accept() en todos los elementos es el mismo, pero puede ser diferente. Por ejemplo, puede haber una lógica para verificar si el artículo es gratuito y luego no llamar al método visit() en absoluto.

ventajas :

  • Si la lógica de la operación cambia, entonces debemos realizar cambios solo en la implementación del visitante en lugar de hacerlo en todas las clases de elementos.
  • Agregar un nuevo elemento al sistema es fácil, requerirá cambios solo en la interfaz de visitante y la implementación, y las clases de elementos existentes no se verán afectadas.

Desventajas:

  • Debemos conocer el tipo de retorno de los métodos visit() al momento de diseñar, de lo contrario, tendremos que cambiar la interfaz y todas sus implementaciones.
  • Si hay demasiadas implementaciones de la interfaz de visitante, será difícil ampliarla.

Otro ejemplo de patrón de visitante en C++

//Write CPP code here
  
#include <iostream>
using namespace std;
  
class Stock
{
  public:
    virtual void accept(class Visitor *) = 0;
      
};
  
class Apple : public Stock
{
  public:
    /*virtual*/ void accept(Visitor *);
    void buy()
    {
        cout << "Apple::buy\n";
    }
    void sell()
    {
        cout << "Apple::sell\n";
    }
      
};
class Google : public Stock
{
  public:
    /*virtual*/ void accept(Visitor *);
    void buy()
    {
        cout << "Google::buy\n";
    }
  
    void sell()
    {
        cout << "Google::sell\n";
    }
};
  
class Visitor
{
  public:
    virtual void visit(Apple *) = 0;
    virtual void visit(Google *) = 0;
    //private:
    static int m_num_apple, m_num_google;
    void total_stocks()
    {
        cout << "m_num_apple " << m_num_apple 
             << ", m_num_google " << m_num_google << '\n';
    }
};
int Visitor::m_num_apple = 0;
int Visitor::m_num_google = 0;
class BuyVisitor : public Visitor
{
  public:
    BuyVisitor()
    {
        m_num_apple = m_num_google = 0;
    }
    /*virtual*/ void visit(Apple *r)
    {
        ++m_num_apple;
        r->buy();
        cout << "m_num_apple " << m_num_apple << endl;
    }
    /*virtual*/ void visit(Google *b)
    {
        ++m_num_google;
        b->buy();
        cout << " m_num_google " << m_num_google << '\n';
    }
};
  
class SellVisitor : public Visitor
{
  public:
    /*virtual*/ void visit(Apple *a)
    {
          
        --m_num_apple;
        a->sell();
        cout << "m_num_apple " << m_num_apple << endl;
    }
    /*virtual*/ void visit(Google *g)
    {
        --m_num_google;
        g->sell();
        cout << "m_num_google " << m_num_google << endl;
    }
};
  
void Apple::accept(Visitor *v)
{
    v->visit(this);
}
  
void Google::accept(Visitor *v)
{
    v->visit(this);
}
  
int main()
{
    Stock *set[] = { new Apple, new Google, new Google,
                     new Apple, new Apple, 0 };
  
    BuyVisitor buy_operation;
    SellVisitor sell_operation;
    for (int i = 0; set[i]; i++)
    {
        set[i]->accept(&buy_operation);
    }
    buy_operation.total_stocks();
  
    for (int i = 0; set[i]; i++)
    {
  
        set[i]->accept(&sell_operation);
    }
    sell_operation.total_stocks();
}
Producción:

Apple::buy
m_num_apple 1
Google::buy
 m_num_google 1
Google::buy
 m_num_google 2
Apple::buy
m_num_apple 2
Apple::buy
m_num_apple 3
m_num_apple 3, m_num_google 2
Apple::sell
m_num_apple 2
Google::sell
m_num_google 1
Google::sell
m_num_google 0
Apple::sell
m_num_apple 1
Apple::sell
m_num_apple 0
m_num_apple 0, m_num_google 0

Este artículo es una contribución de Saket Kumar . Si le gusta GeeksforGeeks y le gustaría contribuir, también puede escribir un artículo usando contribuya.geeksforgeeks.org o envíe su artículo por correo a contribuya@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.

Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *