Visitor Pattern
This lesson discusses the visitor pattern in detail using a coding example.
We'll cover the following
What is the visitor pattern?
The visitor pattern allows defining new operations to the collection of objects without changing the structure of the objects themselves. This allows us to separate the class from the logic it implements.
The extra operations can be encapsulated in a Visitor object. The objects can have a visit method that accepts the Visitor object. The Visitor can then make the required changes and perform the operations on the object that received it. This allows the developers to make future extensions, extend the libraries/frameworks, etc.
Example #
class Visitor {visit(item){}}class BookVisitor extends Visitor {visit(book) {var cost=0;if(book.getPrice() > 50){cost = book.getPrice()*0.50}else{cost = book.getPrice()}console.log("Book name: "+ book.getName() + "\n" + "ID: " + book.getID() + "\n" + "cost: "+ cost);return cost;}}class Book{constructor(id,name,price){this.id = idthis.name = namethis.price = price}getPrice(){return this.price}getName(){return this.name}getID(){return this.id}accept(visitor){return visitor.visit(this)}}var visitor = new BookVisitor()var book1 = new Book("#1234","lordOftheRings",80)book1.accept(visitor)
Explanation
In the example above, we have a book shop. The Book
class is used to represent a book in the shop. It is defined as follows:
class Book{
constructor(id,name,price){
this.id = id
this.name = name
this.price = price
}
//code...
}
A Book
has the following properties:
-
An
id
-
A
name
-
A
price
It also contains the following functions:
getPrice(){
return this.price
}
getName(){
return this.name
}
getID(){
return this.id
}
The getPrice
method returns the price
, getName
returns the name
, and getID
returns the id
of the book.
Now the book shop introduces a discount on the books that cost more than 50 dollars. Hence, now we want to perform an additional operation of visiting the books and implementing the discount on them. Here, we use the visitor pattern. We introduce a Visitor that will visit the books and update their prices. However, the book objects should have some function that allows the visitor to visit them and perform the operation. For this purpose, we have defined the accept
method in our Book
class:
accept(visitor){
return visitor.visit(this)
}
accept
takes a visitor
object as an argument and allows it to visit the current book (this
is pointing to the current book) by calling its visit
function.
Now let’s look at our Visitor
class:
class Visitor {
visit(item){}
}
Visitor
has a visit
function that takes the item
it wants to visit as a parameter. In our case, we want to visit the books; hence, we first define the BookVisitor
class (extends the Visitor
class) and then its visit
function:
class BookVisitor extends Visitor {
visit(book) {
var cost=0;
if(book.getPrice() > 50)
{
cost = book.getPrice()*0.50
}
else{
cost = book.getPrice()
}
console.log("Book name: "+ book.getName() + "\n" + "ID: " + book.getID() + "\n" + "cost: "+ cost);
return cost;
}
}
The visit
function checks the price of the book
it is visiting. If it is greater than 50
, it applies a fifty percent discount to it, else, it keeps the price as it is.
When to use the visitor pattern? #
Visitor pattern can be used when:
-
Similar operations need to be performed on different objects of a data structure.
-
Specific operations need to be performed on different objects in the data structure.
-
You want to add extensibility to libraries or frameworks.
Now that you know what the visitor pattern is, it’s time to implement it!