Friday, 7 June 2019

Design Patterns | Visitor Design Pattern

The intent of the Visitor Design Pattern is to represent an operation to be performed on the elements of an object structure. The Visitor pattern is useful when designing an operation across a heterogeneous collection of objects of a class hierarchy.

What is the Visitor Design Pattern?

The Visitor pattern allows the operation to be defined without changing the class of any of the objects in the collection. To accomplish this, the Visitor pattern suggests defining the operation in a separate class referred to as a visitor class. This separates the operation from the object collection that it operates on. To add a new operation, a new visitor class is needed to be created.

The participant classes in this pattern are:



Visitor
Declares a Visit operation for each class of ConcreteElement in the object structure. The operation’s name and signature identify the class that sends the Visit request to the visitor. That lets the visitor determine the concrete class of the element being visited. Then the visitor can access the element directly through its particular interface.

ConcreteVisitor
Implements each operation declared by the Visitor. Each operation implements a fragment of the algorithm defined for the corresponding class of object in the structure. ConcreteVisitor provides the context for the algorithm and stores its local state. This state often accumulates results during the traversal of the structure.

Visitable(Element)
Defines an Accept operation that takes a visitor as an argument.

ConcreteVisitable(ConcreteElement)
Implements an Accept operation that takes a visitor as an argument.

ObjectStructure
This is a class containing all the objects that can be visited. It offers a mechanism to iterate through all the elements. This structure is not necessarily a collection. In can be a complex structure, such as a composite object.

• Can enumerate its elements.
• May provide a high-level interface to allow the visitor to visit its elements.
• May either be a composite or a collection such as a list or a set.

Example of Visitor design pattern in Java

Think of a Shopping cart where we can add different type of items (Elements). When we click on the checkout button, it calculates the total amount to be paid. The calculation logic in item each item can be different, we can move out this logic to another class using visitor pattern.

/**
 * Accept method takes Visitor argument.
 */
interface ItemElement {
     public int accept(ShoppingCartVisitor visitor);
}

/**
 * Create concrete class for Books.
 *
 * Implementation of accept() method in concrete classes, its calling visit()
 * method of Visitor and passing itself as argument.
 */
class Book implements ItemElement {
     private int price;
     private String isbnNumber;

     public Book(int cost, String isbn) {
           this.price = cost;
           this.isbnNumber = isbn;
     }

     // Getter methods

     @Override
     public int accept(ShoppingCartVisitor visitor) {
           return visitor.visit(this);
     }

}

/**
 * Create concrete class for Fruits.
 *
 * Implementation of accept() method in concrete classes, its calling visit()
 * method of Visitor and passing itself as argument.
 */
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;
     }

     // Getter methods

     @Override
     public int accept(ShoppingCartVisitor visitor) {
           return visitor.visit(this);
     }

}

/**
 * Implementing visit() method for different type of items the Visitor
 * interface.
 */
interface ShoppingCartVisitor {

     int visit(Book book);

     int visit(Fruit fruit);
}

/**
 * Implementation of visitor interface and every item will have it’s own logic
 * to calculate the cost.
 */
class ShoppingCartVisitorImpl implements ShoppingCartVisitor {

     private static int DISCOUNT = 50;

     @Override
     public int visit(Book book) {

           int cost = book.getPrice();
           String bookName = "Book ISBN:: " + book.getIsbnNumber();
           System.out.println(bookName + " cost \t" + cost);

           // If the price is more than 500, the discount of INR 50 offered.
           if (book.getPrice() > 500) {
                cost = cost - DISCOUNT;
           }

           return cost;
     }

     @Override
     public int visit(Fruit fruit) {
           int cost = fruit.getPricePerKg() * fruit.getWeight();
           System.out.println(fruit.getName() + " cost \t\t" + cost);
           return cost;
     }

}

class ShoppingCartVisitorExample {

     public static void main(String[] args) {
           ItemElement[] items = new ItemElement[] { new Book(20, "2342"), new Book(600, "5466"),
                     new Fruit(10, 2, "Banana"), new Fruit(5, 5, "Apple") };

           int total = calculatePrice(items);
           System.out.println("---------------------------------");
           System.out.println("Cart Value  \t\t" + 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;
     }
}

When to use Visitor Design Pattern

• An object structure contains many classes of objects with differing interfaces, and you want to perform operations on these objects that depend on their concrete classes.


• Many distinct and unrelated operations need to be performed on objects in an object structure, and you want to avoid "polluting" their classes with these operations. Visitor lets you keep related operations together by defining them in one class. When the object structure is shared by many applications, use Visitor to put operations in just those applications that need them.

• The classes defining the object structure rarely change, but you often want to define new operations over the structure. Changing the object structure classes requires redefining the interface to all visitors, which is potentially costly. If the object structure classes change often, then it’s probably better to define the operations in those classes.

Visitor Pattern Limitations

The drawback of visitor pattern is that return type of visit() methods should be decided at the time of designing carefully otherwise we will have to change the interface and all of its implementations.

Another drawback is that if there are too many implementations of visitor interface, it makes it hard to extend.

Visitor Design Pattern in JDK

• javax.lang.model.element.Element and javax.lang.model.element.ElementVisitor
• javax.lang.model.type.TypeMirror and javax.lang.model.type.TypeVisitor



No comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...