Visitor
Visitor pattern comes under Behavioral design pattern.
It allows us to define a new operation or algorithm without modifying the the object structure it operates.
Behaviour & Advantages
Represent an operation to be performed on the elements of an object structure.
Separates the algorithm from the object structure it operates.
Lets us define a new operation without changing the classes of the elements on which it operates.
Separates the related operations into a single class (Concrete Visitor) rather than polluting object structure.
Participants
Visitor
Abstract interface for visiting the elements of object structure.
Concrete Visitor
An implementation of Visitor interface.
Element
Abstract interface to represent part or whole of object structure
just like in composite pattern. Each element has to implement
accept operation which takes a visitor as an argument.
Concrete Element
An implementation of Element interface.
Object Structure
It represents an element hierarchy and the root element to be visited.
This example shows a Shopping Cart where its items are visited by PrintShoppingCartVisitor
to print the items and GenerateBillShoppingCartVisitor which generates and prints the bill.
Here IShoppingCartVisitor interface acts as Visitor . Classes PrintShoppingCartVisitor and
GenerateBillShoppingCartVisitor acts as Concrete Visitors . Interface IShoppingCartElement
and classes CartItem, Discount and VAT represents Element and Concrete Elements
respectively. ShoppingCart represents the final Object Structure to be traversed.
Shopping cart element is shown below,
package com.bethecoder.tutorials.dp.visitor;
public interface IShoppingCartElement {
public void accept ( IShoppingCartVisitor shoppingCartVisitor ) ;
}
Shopping cart visitor is shown below,
package com.bethecoder.tutorials.dp.visitor;
public interface IShoppingCartVisitor {
public void visitShoppingCart ( ShoppingCart shoppingCart ) ;
public void visitCartItem ( CartItem cartItem ) ;
public void visitDiscount ( Discount discount ) ;
public void visitVAT ( VAT vat ) ;
}
Print shopping cart concrete visitor.
package com.bethecoder.tutorials.dp.visitor;
public class PrintShoppingCartVisitor implements IShoppingCartVisitor {
@Override
public void visitCartItem ( CartItem cartItem ) {
System.out.println ( cartItem.getItemCount () + " " +
cartItem.getName () .toUpperCase () + "(S) each costing Rs." + cartItem.getPrice ()) ;
}
@Override
public void visitDiscount ( Discount discount ) {
System.out.println ( "Discount : " + discount.getDiscount () + "%" ) ;
}
@Override
public void visitVAT ( VAT vat ) {
System.out.println ( "VAT : " + vat.getVat () + "%" ) ;
}
@Override
public void visitShoppingCart ( ShoppingCart shoppingCart ) {
System.out.println ( "---- PrintShoppingCartVisitor ----" ) ;
for ( int i = 0 ; i < shoppingCart.getCartItems () .size () ; i ++ ) {
shoppingCart.getCartItems () .get ( i ) .accept ( this ) ;
}
}
}
Generate bill concrete visitor.
package com.bethecoder.tutorials.dp.visitor;
public class GenerateBillShoppingCartVisitor implements IShoppingCartVisitor {
@Override
public void visitCartItem ( CartItem cartItem ) {
double itemCost = cartItem.getItemCount () * cartItem.getPrice () ;
cartItem.getShoppingCart () .setTotalBill (
cartItem.getShoppingCart () .getTotalBill () + itemCost ) ;
System.out.println ( cartItem.getItemCount () + " " +
cartItem.getName () .toUpperCase () +
"(" + cartItem.getPrice () + ") - Rs." + itemCost ) ;
}
@Override
public void visitDiscount ( Discount discount ) {
System.out.println ( "\nTotal bill : " + discount.getShoppingCart () .getTotalBill ()) ;
discount.getShoppingCart () .setTotalBill (
discount.getShoppingCart () .getTotalBill () * ( 100 -discount.getDiscount ()) / 100 ) ;
System.out.println ( "After Discount (" + discount.getDiscount () + "%) : " +
discount.getShoppingCart () .getTotalBill ()) ;
}
@Override
public void visitVAT ( VAT vat ) {
System.out.println ( "\nTotal bill : " + vat.getShoppingCart () .getTotalBill ()) ;
vat.getShoppingCart () .setTotalBill (
vat.getShoppingCart () .getTotalBill () * ( 100 +vat.getVat ()) / 100 ) ;
System.out.println ( "After VAT (" + vat.getVat () + "%) : " +
vat.getShoppingCart () .getTotalBill ()) ;
}
@Override
public void visitShoppingCart ( ShoppingCart shoppingCart ) {
System.out.println ( "---- GenerateBillShoppingCartVisitor ----" ) ;
for ( int i = 0 ; i < shoppingCart.getCartItems () .size () ; i ++ ) {
shoppingCart.getCartItems () .get ( i ) .accept ( this ) ;
}
System.out.println ( "\nAmount to pay : " + shoppingCart.getTotalBill ()) ;
}
}
Shopping cart item concrete element.
package com.bethecoder.tutorials.dp.visitor;
public class CartItem implements IShoppingCartElement {
private String name;
private int itemCount;
private double price;
private ShoppingCart shoppingCart;
public CartItem ( String name, int itemCount, double price ) {
super () ;
this .name = name;
this .itemCount = itemCount;
this .price = price;
}
@Override
public void accept ( IShoppingCartVisitor shoppingCartVisitor ) {
shoppingCartVisitor.visitCartItem ( this ) ;
}
public double getPrice () {
return price;
}
public void setPrice ( double price ) {
this .price = price;
}
public String getName () {
return name;
}
public void setName ( String name ) {
this .name = name;
}
public int getItemCount () {
return itemCount;
}
public void setItemCount ( int itemCount ) {
this .itemCount = itemCount;
}
public ShoppingCart getShoppingCart () {
return shoppingCart;
}
public void setShoppingCart ( ShoppingCart shoppingCart ) {
this .shoppingCart = shoppingCart;
}
}
Discount concrete element.
package com.bethecoder.tutorials.dp.visitor;
public class Discount implements IShoppingCartElement {
private int discount;
private ShoppingCart shoppingCart;
public Discount ( int discount ) {
super () ;
this .discount = discount;
}
@Override
public void accept ( IShoppingCartVisitor shoppingCartVisitor ) {
shoppingCartVisitor.visitDiscount ( this ) ;
}
public int getDiscount () {
return discount;
}
public void setDiscount ( int discount ) {
this .discount = discount;
}
public ShoppingCart getShoppingCart () {
return shoppingCart;
}
public void setShoppingCart ( ShoppingCart shoppingCart ) {
this .shoppingCart = shoppingCart;
}
}
VAT concrete element.
package com.bethecoder.tutorials.dp.visitor;
public class VAT implements IShoppingCartElement {
private int vat;
private ShoppingCart shoppingCart;
public VAT ( int vat ) {
super () ;
this .vat = vat;
}
@Override
public void accept ( IShoppingCartVisitor shoppingCartVisitor ) {
shoppingCartVisitor.visitVAT ( this ) ;
}
public int getVat () {
return vat;
}
public void setVat ( int vat ) {
this .vat = vat;
}
public ShoppingCart getShoppingCart () {
return shoppingCart;
}
public void setShoppingCart ( ShoppingCart shoppingCart ) {
this .shoppingCart = shoppingCart;
}
}
Shopping Cart object structure.
package com.bethecoder.tutorials.dp.visitor;
import java.util.ArrayList;
import java.util.List;
public class ShoppingCart implements IShoppingCartElement {
private List<IShoppingCartElement> cartItems =
new ArrayList<IShoppingCartElement> () ;
private double totalBill;
public void addCartItem ( CartItem shoppingCartElement ) {
shoppingCartElement.setShoppingCart ( this ) ;
cartItems.add ( shoppingCartElement ) ;
}
public void addVAT () {
VAT vat = new VAT ( 14 ) ;
vat.setShoppingCart ( this ) ;
cartItems.add ( vat ) ;
}
public void giveDiscount () {
Discount discount = new Discount ( 10 ) ;
discount.setShoppingCart ( this ) ;
cartItems.add ( discount ) ;
}
@Override
public void accept ( IShoppingCartVisitor shoppingCartVisitor ) {
shoppingCartVisitor.visitShoppingCart ( this ) ;
}
public List<IShoppingCartElement> getCartItems () {
return cartItems;
}
public void setCartItems ( List<IShoppingCartElement> cartItems ) {
this .cartItems = cartItems;
}
public double getTotalBill () {
return totalBill;
}
public void setTotalBill ( double totalBill ) {
this .totalBill = totalBill;
}
}
Visitor usage is shown below,
package com.bethecoder.tutorials.dp.visitor;
public class Test {
/**
* @param args
*/
public static void main ( String [] args ) {
ShoppingCart shoppingCart = new ShoppingCart () ;
shoppingCart.addCartItem ( new CartItem ( "Apple" , 4 , 12.50 )) ;
shoppingCart.addCartItem ( new CartItem ( "Mango" , 6 , 24.50 )) ;
shoppingCart.addCartItem ( new CartItem ( "Orange" , 8 , 6.50 )) ;
shoppingCart.giveDiscount () ;
shoppingCart.addVAT () ;
shoppingCart.accept ( new PrintShoppingCartVisitor ()) ;
System.out.println () ;
shoppingCart.accept ( new GenerateBillShoppingCartVisitor ()) ;
}
}
It gives the following output,
---- PrintShoppingCartVisitor ----
4 APPLE(S) each costing Rs.12.5
6 MANGO(S) each costing Rs.24.5
8 ORANGE(S) each costing Rs.6.5
Discount : 10%
VAT : 14%
---- GenerateBillShoppingCartVisitor ----
4 APPLE(12.5) - Rs.50.0
6 MANGO(24.5) - Rs.147.0
8 ORANGE(6.5) - Rs.52.0
Total bill : 249.0
After Discount (10%) : 224.1
Total bill : 224.1
After VAT (14%) : 255.474
Amount to pay : 255.474