Implementation of the Visitor pattern

In this post, we will look at the implementation of the Visitor pattern using Java. Our use case is of an online shop. You choose different items to buy and for each item, an additional cost is applied if the item is over a certain weight. Let’s start!

First of all, we will create our item classes. These items will be Book and CD. Both of these classes are children of a parent class called Item.

public abstract class Item implements Visitable {
private double weight;
private double price;
private double finalPrice;
public Item(double weight, double price) {
this.weight = weight;
this.price = price;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public double getPrice() {
return this.price;
}
public void setPrice(double price) {
this.price = price;
}
public double getFinalPrice() {
return this.finalPrice;
}
public void setFinalPrice(double finalPrice) {
this.finalPrice = finalPrice;
}
}

You probably noticed that the Item class implmenets an interface called Visitable. The purpose of that interface is exactly as the name says. It makes the class that implements the interface visitable by some specific visitors. How? Let’s continue over. We will now create our Book class that extends the Item class above.

public class Book extends Item {    public Book(double weight, double price) {
super(weight, price);
}
@Override
public void accept(WeightVisitor visitor) {
visitor.visit(this);
}
@Override
public void accept(TotalPriceVisitor visitor) {
visitor.visit(this);
}
}

As you probably discovered it by now, the Visitable interface has two methods called accept. One of them accepts a visitor of type WeightVisitor and the other accepts a visitor of type TotalPriceVisitor.

public interface Visitable {
public abstract void accept(WeightVisitor visitor);
public abstract void accept(TotalPriceVisitor visitor);
}

Now, let’s show the implementation of the CD class which is similar to the Book class.

public class CD extends Item {    public CD(double weight, double price) {
super(weight, price);
}
@Override
public void accept(WeightVisitor visitor) {
visitor.visit(this);
}
@Override
public void accept(TotalPriceVisitor visitor) {
visitor.visit(this);
}
}

We have seen almost all the pieces of our puzzle, except probably the most important one. THE VISITOR! What is the Visitor? Have a look at the code below.

public interface Visitor {
public void visit(Book book);
public void visit(CD cd);
}

At first, this looks as a simple interface but then, you understand that it really is. It is a simple interface that declares two methods should be implemented by every concrete class visitor. Here is how it is done.

public class WeightVisitor implements Visitor {    @Override
public void visit(Book book) {
if (book.getWeight() > 10) {
book.setFinalPrice(book.getPrice() + 200);
} else {
book.setFinalPrice(book.getPrice());
}
}
@Override
public void visit(CD cd) {
if (cd.getWeight() > 10) {
cd.setFinalPrice(cd.getPrice() + 140);
} else {
cd.setFinalPrice(cd.getPrice());
}
}
}

The WeightVisitor implements both methods defined by the Visitor interface and fills these methods with the logic specific to the type of object the visitor will be visiting.

public class TotalPriceVisitor implements Visitor {
private double totalPrice;
@Override
public void visit(Book book) {
totalPrice = totalPrice + book.getFinalPrice();
}
@Override
public void visit(CD cd) {
totalPrice = totalPrice + cd.getFinalPrice();
}
public double getTotalPrice() {
return this.totalPrice;
}
}

We have all the pieces of puzzle. Now it’s time to run the app.

import java.util.ArrayList;
import java.util.List;
public class Application { public static void main(String[] args) {
Book book1 = new Book(4, 40);
Book book2 = new Book(50, 40);
CD cd = new CD(1, 20);
List<Item> itemList = new ArrayList<>();
itemList.add(book1);
itemList.add(book2);
itemList.add(cd);
WeightVisitor weightVisitor = new WeightVisitor();
TotalPriceVisitor totalPriceVisitor = new TotalPriceVisitor();
for (Item item : itemList) {
item.accept(weightVisitor);
item.accept(totalPriceVisitor);
}
System.out.println(book1.getFinalPrice());
System.out.println(totalPriceVisitor.getTotalPrice());
}
}

As you can see, we create two different visitors and while looping through the list of items in our shopping cart, we allow these visitors to visit the items and run their logic based on the properties of the object they are visiting.

For more similar content, visit https://lejdiprifti.web.app