Visitor Pattern

Visitor Pattern is one of the behavioral design pattern which is used when we have to manage algorithms, relationships & responsibilities on a group of similar kind of objects at runtime, thus decoupling the operations from the object’s structure. So, Visitor pattern can provide additional functionality to a class without actually changing it.

The pattern should be used when you have distinct & unrelated operations to perform across a structure of objects. This avoids adding in code throughout your object structure that is better kept separate, so it encourages cleaner code. In summary, if you want to decouple some logical code from the elements that you’re using as input, visitor is probably the best pattern for the job.

On the downside, the arguments & return types for the visiting methods needs to be known in advance, so the Visitor pattern is not good for situations where these visited classes are subject to change. Every time a new type of Element is added, every Visitor derived class must be amended.

Also, it can be difficult to refactor the Visitor pattern into code that wasn’t already designed with the pattern in mind. And, when you do add your Visitor code, it can look obscure. The Visitor is powerful, but you should make sure to use it only when necessary.

Let’s try to illustrate the pattern with an example. Below is the class diagram for ease of reference –

classdiagram_1

First, let’s create our general visitable  interface:


package com.sanjit;

public interface ShoppingItem {
      public void accept(Visitor visitor);
}

  

Now, we’ll create a concrete implementation of our interface, Games :


package com.sanjit;

public class Games implements ShoppingItem {

      private double price;
      private int qty;

      public double getPrice() {
            return price;
      }

      public void setPrice(double price) {
            this.price = price;
      }

      public int getQty() {
            return qty;
      }

      public void setQty(int qty) {
            this.qty = qty;
      }

      @Override
      public void accept(Visitor visitor) {
            visitor.visit(this);
      }
}

  

and now for the Music:


package com.sanjit;

public class Music implements ShoppingItem {

      private double price;
      private int qty;

      public double getPrice() {
            return price;
      }

      public void setPrice(double price) {
            this.price = price;
      }

      public int getQty() {
            return qty;
      }

      public void setQty(int qty) {
            this.qty = qty;
      }

      @Override
      public void accept(Visitor visitor) {
            visitor.visit(this);
      }
}

As you can see it’s just a simple POJO, with the extra accept method added to allow the visitor access to the element. We could add in other types here to handle other items.

Now we’ll move on to the Visitor interface. For each different type of concrete element here, we’ll need to add a visit method. As we’ll just deal with Games & Music for now, this is as simple as:


package com.sanjit;

public interface Visitor {
      void visit(Games items);
      void visit(Music items);
}

The implementation of the Visitor can then deal with the specifics of what to do when we visit a Game or Music.


package com.sanjit;

public class CartVisitor implements Visitor {

      private double cartCost;

      public double getCartCost() {
            return cartCost;
      }

      public void setCartCost(double cartCost) {
            this.cartCost = cartCost;
      }

      @Override
      public void visit(Music items) {
         cartCost += items.getQty() * items.getPrice();
      }

      @Override
      public void visit(Games items) {
            cartCost += items.getQty() * items.getPrice();
      }
}

As you can see it’s a simple formula, but the point is that all the calculation for cart is done in one central place.

To drive this visitor, we’ll need a way of iterating through our shopping cart, as follows:


package com.sanjit;

import java.util.List;

public class Cart {

      private List items;

      public List getItems() {
            return items;
      }

      public void setItems(List items) {
            this.items = items;
      }

      public void calculateCartPrice() {

            CartVisitor cartVisitor = new CartVisitor();

            for(ShoppingItem item:items){
                  item.accept(cartVisitor);
            }

            System.out.println("Total Cart Cost: " + cartVisitor.getCartCost());
      }
}

Note that if we had other types of item here, once the visitor implements a method to visit that item, we could easily calculate the total cart cost.

The whole point of this pattern is to allow you separate out certain logic from the elements themselves, keeping your data classes simple.

Now the main class for test purpose:-


package com.sanjit;

import java.util.ArrayList;

import java.util.List;

public class Main {

          public static void main(String[] args) {

            Music m = new Music();
            m.setPrice(50);
            m.setQty(10);

            Games g = new Games();
            g.setPrice(100);
            g.setQty(100);

            List items = new ArrayList();
            items.add(m);
            items.add(g);

            Cart cart = new Cart();
            cart.setItems(items);
            cart.calculateCartPrice();
      }

}

Output on running the Main class:

Total Cart Cost: 10500.0