DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Object Relational Behavioral Design Patterns in Java
  • Distribution Design Patterns in Java - Data Transfer Object (DTO) And Remote Facade Design Patterns
  • Context Object Design Pattern in Java: Introduction and Key Points
  • Java: Object Pool Design Pattern

Trending

  • Scaling InfluxDB for High-Volume Reporting With Continuous Queries (CQs)
  • Streamlining Event Data in Event-Driven Ansible
  • Emerging Data Architectures: The Future of Data Management
  • Rust and WebAssembly: Unlocking High-Performance Web Apps
  1. DZone
  2. Coding
  3. Languages
  4. Visitor Design Pattern In Java

Visitor Design Pattern In Java

By 
Brijesh Saxena user avatar
Brijesh Saxena
DZone Core CORE ·
Oct. 13, 20 · Tutorial
Likes (16)
Comment
Save
Tweet
Share
7.6K Views

Join the DZone community and get the full member experience.

Join For Free

Today, I am here to discuss another behavioral design pattern called Visitor Design Pattern. The Visitor design pattern lets us separate algorithms from the objects on which they operate.

Visitor Design Pattern

  •  The Visitor Design Pattern provides a way of separating an algorithm from an object structure on which it operates.
  • A practical result of this separation is the ability to add new operations to existing object structures without modifying the structures. It is one way to follow the open/closed principle. 
  • The Visitor Design Pattern is used when we like to perform an operation on a group/family of objects.
  • The Visitor Design Pattern is one of the twenty-three well-known GoF design patterns  which helps us to move the operational logic from the objects to another class.
  • The Visitor allows adding new virtual functions to a family of classes, without modifying the classes.
  • Instead, a visitor class is created that implements all of the appropriate specializations of the virtual function.  
  • The Visitor Design Pattern makes it possible to define a new operation for (some) classes of an object structure without changing the classes.
  • When new operations are needed frequently and the object structure consists of many unrelated classes, it's inflexible to add new sub-classes each time a new operation is required.
  • The Element accepts the visitor and delegates the operation to the accepted Visitor object.
  • The Visitor object performs the operation on the element ("visits the element").
  • The Visitor Design Pattern makes it possible to create new operations independently from the classes of an object structure by adding new visitor objects.


visitor design pattern UML diagram

To understand this, let's take an example of a Shop, which sells books, fruits, vegetables, and electronics. 

Shop Bill Processing Application Example

 Lets first define ShopItemCategory enum:

Java
xxxxxxxxxx
1
18
 
1
package org.trishinfotech.visitor.example1.items;
2
3
public enum ShopItemCategory {
4
5
    BOOK("Book"), ELECTRONICS("Electronics"), FRUIT("Fruit"), VEGETABLE("Vegetable");
6
7
    private String name;
8
9
    ShopItemCategory(String name) {
10
        this.name = name;
11
    }
12
13
    public String getName() {
14
        return name;
15
    }
16
17
}


Now, define the ShopItem interface:

Java
xxxxxxxxxx
1
13
 
1
package org.trishinfotech.visitor.example1.items;
2
3
public interface ShopItem {
4
5
    String getName();
6
7
    double getPrice();
8
9
    double getWeight();
10
11
    ShopItemCategory getCategory();
12
}


Now, define the concrete-shop-item class. Code for Book class:

Java
xxxxxxxxxx
1
59
 
1
package org.trishinfotech.visitor.example1.items;
2
3
public class Book implements ShopItem {
4
5
    private String title;
6
    private double pricePerUnit;
7
    
8
    public Book(String title, double pricePerUnit) {
9
        this.title = title;
10
        this.pricePerUnit = pricePerUnit;
11
    }
12
13
    public String getTitle() {
14
        return title;
15
    }
16
    
17
    public void setTitle(String title) {
18
        this.title = title;
19
    }
20
    
21
    public double getPricePerUnit() {
22
        return pricePerUnit;
23
    }
24
25
    public void setPricePerUnit(int price) {
26
        this.pricePerUnit = price;
27
    }
28
29
    @Override
30
    public String getName() {
31
        return getTitle();
32
    }
33
34
    @Override
35
    public double getPrice() {
36
        return getPricePerUnit();
37
    }
38
39
    @Override
40
    public double getWeight() {
41
        return 0.0d;
42
    }
43
    
44
    @Override
45
    public ShopItemCategory getCategory() {
46
        return ShopItemCategory.BOOK;
47
    }
48
49
    @Override
50
    public String toString() {
51
        StringBuilder builder = new StringBuilder();
52
        builder.append("Book [title=").append(title)
53
            .append(", price=").append(getPrice())
54
            .append("]");
55
        return builder.toString();
56
    }
57
    
58
}


Code for Electronics class:

Java
xxxxxxxxxx
1
55
 
1
package org.trishinfotech.visitor.example1.items;
2
3
public class Electronics implements ShopItem {
4
5
    private String brand;
6
    private double price;
7
    
8
    public Electronics(String brand, double price) {
9
        this.brand = brand;
10
        this.price = price;
11
    }
12
    
13
    public String getBrand() {
14
        return brand;
15
    }
16
17
    public void setBrand(String brand) {
18
        this.brand = brand;
19
    }
20
21
    public void setPrice(double price) {
22
        this.price = price;
23
    }
24
    
25
    @Override
26
    public String getName() {
27
        return getBrand();
28
    }
29
    
30
    @Override
31
    public double getPrice() {
32
        return price;
33
    }
34
35
    @Override
36
    public double getWeight() {
37
        return 0.0d;
38
    }
39
    
40
    @Override
41
    public ShopItemCategory getCategory() {
42
        return ShopItemCategory.ELECTRONICS;
43
    }
44
    
45
    @Override
46
    public String toString() {
47
        StringBuilder builder = new StringBuilder();
48
        builder.append("Electronics [brand=").append(brand)
49
            .append(", price=").append(price)
50
            .append("]");
51
        return builder.toString();
52
    }
53
54
}
55


Code for Fruit class:

Java
xxxxxxxxxx
1
51
 
1
package org.trishinfotech.visitor.example1.items;
2
3
public class Fruit implements ShopItem {
4
5
    private String name;
6
    private double pricePerKg;
7
    private double weight;
8
    
9
    public Fruit(String name, double pricePerKg, double weight) {
10
        this.name = name;
11
        this.pricePerKg = pricePerKg;
12
        this.weight = weight;
13
    }
14
15
    public double getPricePerKg() {
16
        return pricePerKg;
17
    }
18
    
19
    @Override
20
    public String getName() {
21
        return this.name;
22
    }
23
24
    @Override
25
    public double getPrice() {
26
        return getPricePerKg();
27
    }
28
    
29
    @Override
30
    public double getWeight() {
31
        return weight;
32
    }
33
34
    @Override
35
    public ShopItemCategory getCategory() {
36
        return ShopItemCategory.FRUIT;
37
    }
38
    
39
    @Override
40
    public String toString() {
41
        StringBuilder builder = new StringBuilder();
42
        builder.append("Fruit [name=").append(name)
43
            .append(", pricePerKg=").append(pricePerKg)
44
            .append(", weight=").append(weight)
45
            .append("]");
46
        return builder.toString();
47
    }
48
49
    
50
}


and code for Vegetable class:

Java
xxxxxxxxxx
1
58
 
1
package org.trishinfotech.visitor.example2.items;
2
3
import org.trishinfotech.visitor.example2.ShopVisitor;
4
5
public class Vegetable implements ShopItem {
6
7
    private String name;
8
    private double pricePerKg;
9
    private double weight;
10
    
11
    public Vegetable(String name, double pricePerKg, double weight) {
12
        this.name = name;
13
        this.pricePerKg = pricePerKg;
14
        this.weight = weight;
15
    }
16
17
    public double getPricePerKg() {
18
        return pricePerKg;
19
    }
20
    
21
    @Override
22
    public String getName() {
23
        return this.name;
24
    }
25
    
26
    @Override
27
    public double getPrice() {
28
        return getPricePerKg();
29
    }
30
    
31
    @Override
32
    public double getWeight() {
33
        return weight;
34
    }
35
    
36
    @Override
37
    public double accept(ShopVisitor visitor) {
38
        return visitor.visit(this);
39
    }
40
    
41
    @Override
42
    public ShopItemCategory getCategory() {
43
        return ShopItemCategory.VEGETABLE;
44
    }
45
46
    @Override
47
    public String toString() {
48
        StringBuilder builder = new StringBuilder();
49
        builder.append("Vegetable [name=").append(name)
50
            .append(", pricePerKg=").append(pricePerKg)
51
            .append(", weight=").append(weight)
52
            .append("]");
53
        return builder.toString();
54
    }
55
56
    
57
}


Since the shop get customers, let's define a Customer class:

Java
xxxxxxxxxx
1
29
 
1
package org.trishinfotech.visitor.example1;
2
3
import org.trishinfotech.visitor.example1.items.ShopItem;
4
5
public abstract class Customer {
6
7
    protected String name;
8
    
9
    public Customer(String name) {
10
        super();
11
        this.name = name;
12
    }
13
14
    public double calculateCost(ShopItem item) {
15
        double price = item.getPrice();
16
        double weight = item.getWeight();
17
        return (weight == 0.0d) ? price : price * weight;
18
    }
19
20
    public String getName() {
21
        return name;
22
    }
23
24
    public void setName(String name) {
25
        this.name = name;
26
    }
27
28
}


Here, I have added a method to calculate cost of the shop item as well. Also, I made the class abstract to support different concrete customer classes.

Code for Person class:

Java
xxxxxxxxxx
1
15
 
1
package org.trishinfotech.visitor.example1;
2
3
public class Person extends Customer {
4
5
    public Person(String name) {
6
        super(name);
7
    }
8
9
    @Override
10
    public String toString() {
11
        return "Person (" + name + ")";
12
    }
13
14
}


And code for Student class:

Java
xxxxxxxxxx
1
15
 
1
package org.trishinfotech.visitor.example1;
2
3
public class Student extends Customer {
4
5
    public Student(String name) {
6
        super(name);
7
    }
8
9
    @Override
10
    public String toString() {
11
        return "Student (" + name + ")";
12
    }
13
    
14
}


Now code for a utility class, BillPrinter, for printing itemized bill:

Java
xxxxxxxxxx
1
43
 
1
package org.trishinfotech.visitor.example1.util;
2
3
import java.util.Comparator;
4
import java.util.List;
5
import java.util.concurrent.atomic.DoubleAdder;
6
7
import org.trishinfotech.visitor.example1.Customer;
8
import org.trishinfotech.visitor.example1.items.ShopItem;
9
10
public class BillPrinter {
11
12
    public static double calculateAndPrintBill(Customer customer, List<ShopItem> items) {
13
        double totalCost = printItemisedBill(customer, items);
14
        System.out.printf("Total Cost = %69.2f\n", totalCost);
15
        System.out.println(
16
                "============================================================================================================");
17
        return totalCost;
18
    }
19
20
    public static double printItemisedBill(Customer customer, List<ShopItem> items) {
21
        items.sort(Comparator.comparing(ShopItem::getCategory).thenComparing(ShopItem::getName));
22
        System.out.printf("                                Shopping Items Bill for the %s\n", customer);
23
        System.out.println(
24
                "============================================================================================================");
25
        System.out.printf("%25s | %15s | %10s | %10s | %10s\n", "Shopping Item", "Category", "Price", "Weight", "Cost");
26
        System.out.println(
27
                "------------------------------------------------------------------------------------------------------------");
28
        DoubleAdder totalCost = new DoubleAdder();
29
        items.stream().forEach(item -> {
30
            String name = item.getName();
31
            double price = item.getPrice();
32
            double weight = item.getWeight();
33
            double cost = customer.calculateCost(item);
34
            totalCost.add(cost);
35
            System.out.printf("%25s | %15s | %10.2f | %10s | %10.2f\n", name, item.getCategory().getName(),
36
                    price, (weight > 0.0d) ? weight : "-", cost);
37
        });
38
        System.out.println(
39
                "============================================================================================================");
40
        return totalCost.doubleValue();
41
    }
42
}


Now, it's time to write the Main program to execute and test the output:

Java
xxxxxxxxxx
1
119
 
1
package org.trishinfotech.visitor.example1;
2
3
import java.util.ArrayList;
4
import java.util.HashMap;
5
import java.util.List;
6
import java.util.Map;
7
8
import org.trishinfotech.visitor.example1.items.Book;
9
import org.trishinfotech.visitor.example1.items.Electronics;
10
import org.trishinfotech.visitor.example1.items.Fruit;
11
import org.trishinfotech.visitor.example1.items.ShopItem;
12
import org.trishinfotech.visitor.example1.items.ShopItemCategory;
13
import org.trishinfotech.visitor.example1.items.Vegetable;
14
import org.trishinfotech.visitor.example1.util.BillPrinter;
15
16
@SuppressWarnings("serial")
17
public class Main {
18
19
    protected static Map<ShopItemCategory, Map<String, Double>> shopItemsPriceList = new HashMap<ShopItemCategory, Map<String, Double>>();
20
21
    static {
22
        shopItemsPriceList.put(ShopItemCategory.BOOK, new HashMap<String, Double>() {
23
            {
24
                put("Java - Design Patterns", 10.4d);
25
                put("Learn java in 24 days", 8.64d);
26
                put("William Shakespeare", 12.43d);
27
                put("Times Magazine", 8.76d);
28
            }
29
        });
30
        shopItemsPriceList.put(ShopItemCategory.FRUIT, new HashMap<String, Double>() {
31
            {
32
                put("Banana", 2.0d);
33
                put("Apple", 3.5d);
34
                put("Mango", 5.5d);
35
                put("Grape", 4.3d);
36
            }
37
        });
38
        shopItemsPriceList.put(ShopItemCategory.VEGETABLE, new HashMap<String, Double>() {
39
            {
40
                put("Potato", 5.0d);
41
                put("Tomato", 1.4d);
42
                put("Onion", 2.43d);
43
                put("Capsicum", 3.76d);
44
            }
45
        });
46
        shopItemsPriceList.put(ShopItemCategory.ELECTRONICS, new HashMap<String, Double>() {
47
            {
48
                put("Casio Calculator", 4.3d);
49
                put("Samsung Mobile", 410.65d);
50
                put("iPhone X", 650.36d);
51
                put("MacBook Pro", 3500.45d);
52
            }
53
        });
54
    }
55
56
    public static void main(String[] args) {
57
        Customer student = new Student("Racheal"); // Visitor #1
58
        List<ShopItem> studentItems = prepareShopItems(new HashMap<String, Double>() {
59
            {
60
                put("Java - Design Patterns", 1.0d);
61
                put("Learn java in 24 days", 8.64d);
62
                put("Banana", 2.3d);
63
                put("Apple", 1.4d);
64
                put("Potato", 2.0d);
65
                put("Tomato", 1.5d);
66
                put("Casio Calculator", 1.0d);
67
                put("iPhone X", 1.0d);
68
            }
69
        });
70
        double totalCostForStudent = BillPrinter.calculateAndPrintBill(student, studentItems);
71
        System.out.printf("Amount to pay = %10.2f\n", totalCostForStudent);
72
73
        System.out.println("\n\n");
74
75
        Customer person = new Person("Micheal"); // Visitor #2
76
        List<ShopItem> personItems = prepareShopItems(new HashMap<String, Double>() {
77
            {
78
                put("Times Magazine", 1.0d);
79
                put("William Shakespeare", 1.0d);
80
                put("Banana", 2.8d);
81
                put("Mango", 1.4d);
82
                put("Potato", 2.8d);
83
                put("Tomato", 2.5d);
84
                put("Samsung Mobile", 1.0d);
85
                put("MacBook Pro", 1.0d);
86
            }
87
        });
88
        double totalCostForPerson = BillPrinter.calculateAndPrintBill(person, personItems);
89
        System.out.printf("Amount to pay = %10.2f\n", totalCostForPerson);
90
    }
91
92
    private static List<ShopItem> prepareShopItems(HashMap<String, Double> hashMap) {
93
        List<ShopItem> shopItems = new ArrayList<ShopItem>();
94
        hashMap.forEach((item, quantity) -> {
95
            for (ShopItemCategory category : ShopItemCategory.values()) {
96
                Double price = shopItemsPriceList.get(category).get(item);
97
                if (price != null) {
98
                    switch (category) {
99
                    case BOOK:
100
                        shopItems.add(new Book(item, price));
101
                        break;
102
                    case FRUIT:
103
                        shopItems.add(new Fruit(item, price, quantity));
104
                        break;
105
                    case VEGETABLE:
106
                        shopItems.add(new Vegetable(item, price, quantity));
107
                        break;
108
                    case ELECTRONICS:
109
                        shopItems.add(new Electronics(item, price));
110
                        break;
111
                    }
112
                    break;
113
                }
114
            }
115
116
        });
117
        return shopItems;
118
    }
119
}


and below is the output:

Java
xxxxxxxxxx
1
36
 
1
                                Shopping Items Bill for the Student (Racheal)
2
============================================================================================================
3
            Shopping Item |        Category |      Price |     Weight |       Cost
4
------------------------------------------------------------------------------------------------------------
5
   Java - Design Patterns |            Book |      10.40 |          - |      10.40
6
    Learn java in 24 days |            Book |       8.64 |          - |       8.64
7
         Casio Calculator |     Electronics |       4.30 |          - |       4.30
8
                 iPhone X |     Electronics |     650.36 |          - |     650.36
9
                    Apple |           Fruit |       3.50 |        1.4 |       4.90
10
                   Banana |           Fruit |       2.00 |        2.3 |       4.60
11
                   Potato |       Vegetable |       5.00 |        2.0 |      10.00
12
                   Tomato |       Vegetable |       1.40 |        1.5 |       2.10
13
============================================================================================================
14
Total Cost =                                                                695.30
15
============================================================================================================
16
Amount to pay =     695.30
17
18
19
20
                                Shopping Items Bill for the Person (Micheal)
21
============================================================================================================
22
            Shopping Item |        Category |      Price |     Weight |       Cost
23
------------------------------------------------------------------------------------------------------------
24
           Times Magazine |            Book |       8.76 |          - |       8.76
25
      William Shakespeare |            Book |      12.43 |          - |      12.43
26
              MacBook Pro |     Electronics |    3500.45 |          - |    3500.45
27
           Samsung Mobile |     Electronics |     410.65 |          - |     410.65
28
                   Banana |           Fruit |       2.00 |        2.8 |       5.60
29
                    Mango |           Fruit |       5.50 |        1.4 |       7.70
30
                   Potato |       Vegetable |       5.00 |        2.8 |      14.00
31
                   Tomato |       Vegetable |       1.40 |        2.5 |       3.50
32
============================================================================================================
33
Total Cost =                                                               3963.09
34
============================================================================================================
35
Amount to pay =    3963.09


So far so good. Now, suppose the shop-owner wants to start supporting discounts on different shop-items based on item-category, item quantity/price, and customer type (student or common person).

To minimize the changes in the shop-item classes, we will use the Visitor pattern here. Visitor pattern will handle calculating the discount for each of the shop items.

Shop Bill Processing Application Example Using Visitor Design Pattern

Let's start updating the code to implement discount calculation functionality using Visitor pattern.

Code for ShopItemCategory enum:

Java
xxxxxxxxxx
1
18
 
1
package org.trishinfotech.visitor.example2.items;
2
3
public enum ShopItemCategory {
4
    
5
    BOOK("Book"), ELECTRONICS("Electronics"), FRUIT("Fruit"), VEGETABLE("Vegetable");
6
7
    private String name;
8
9
    ShopItemCategory(String name) {
10
        this.name = name;
11
    }
12
13
    public String getName() {
14
        return name;
15
    }
16
17
}


Updated code for ShopItem interface:

Java
xxxxxxxxxx
1
20
 
1
package org.trishinfotech.visitor.example2.items;
2
3
import org.trishinfotech.visitor.example2.ShopVisitor;
4
5
public interface ShopItem {
6
7
    String getName();
8
9
    double getPrice();
10
11
    double getWeight();
12
13
    ShopItemCategory getCategory();
14
15
    // will accept different visitors
16
    // every visitor type will have its own defined discount
17
    // and hence will differ the final cost of the item.
18
    double accept(ShopVisitor visitor);
19
}


Since the interface will act as an Element for us, I have added the method accept() to accept the visitor object. The Visitor object will perform the operation of calculating discounts for each item. Now, we have to implement the accept() method in each concrete shop-item class.

Updated code for Book class:

Java
xxxxxxxxxx
1
59
 
1
package org.trishinfotech.visitor.example1.items;
2
3
public class Book implements ShopItem {
4
5
    private String title;
6
    private double pricePerUnit;
7
    
8
    public Book(String title, double pricePerUnit) {
9
        this.title = title;
10
        this.pricePerUnit = pricePerUnit;
11
    }
12
13
    public String getTitle() {
14
        return title;
15
    }
16
    
17
    public void setTitle(String title) {
18
        this.title = title;
19
    }
20
    
21
    public double getPricePerUnit() {
22
        return pricePerUnit;
23
    }
24
25
    public void setPricePerUnit(int price) {
26
        this.pricePerUnit = price;
27
    }
28
29
    @Override
30
    public String getName() {
31
        return getTitle();
32
    }
33
34
    @Override
35
    public double getPrice() {
36
        return getPricePerUnit();
37
    }
38
39
    @Override
40
    public double getWeight() {
41
        return 0.0d;
42
    }
43
    
44
    @Override
45
    public ShopItemCategory getCategory() {
46
        return ShopItemCategory.BOOK;
47
    }
48
49
    @Override
50
    public String toString() {
51
        StringBuilder builder = new StringBuilder();
52
        builder.append("Book [title=").append(title)
53
            .append(", price=").append(getPrice())
54
            .append("]");
55
        return builder.toString();
56
    }
57
    
58
}


Updated code for Electronics class:

Java
xxxxxxxxxx
1
63
 
1
package org.trishinfotech.visitor.example2.items;
2
3
import org.trishinfotech.visitor.example2.ShopVisitor;
4
5
public class Electronics implements ShopItem {
6
7
    private String brand;
8
    private double price;
9
    
10
    public Electronics(String brand, double price) {
11
        this.brand = brand;
12
        this.price = price;
13
    }
14
    
15
    public String getBrand() {
16
        return brand;
17
    }
18
19
    public void setBrand(String brand) {
20
        this.brand = brand;
21
    }
22
23
    public void setPrice(double price) {
24
        this.price = price;
25
    }
26
    
27
    @Override
28
    public String getName() {
29
        return getBrand();
30
    }
31
    
32
    @Override
33
    public double getPrice() {
34
        return price;
35
    }
36
37
    @Override
38
    public double getWeight() {
39
        return 0.0d;
40
    }
41
    
42
    @Override
43
    public ShopItemCategory getCategory() {
44
        return ShopItemCategory.ELECTRONICS;
45
    }
46
    
47
    @Override
48
    public double accept(ShopVisitor visitor) {
49
        return visitor.visit(this);
50
    }
51
52
    @Override
53
    public String toString() {
54
        StringBuilder builder = new StringBuilder();
55
        builder.append("Electronics [brand=").append(brand)
56
            .append(", price=").append(price)
57
            .append("]");
58
        return builder.toString();
59
    }
60
61
    
62
}


Updated code for Fruit class:

Java
xxxxxxxxxx
1
58
 
1
package org.trishinfotech.visitor.example2.items;
2
3
import org.trishinfotech.visitor.example2.ShopVisitor;
4
5
public class Fruit implements ShopItem {
6
7
    private String name;
8
    private double pricePerKg;
9
    private double weight;
10
    
11
    public Fruit(String name, double pricePerKg, double weight) {
12
        this.name = name;
13
        this.pricePerKg = pricePerKg;
14
        this.weight = weight;
15
    }
16
17
    public double getPricePerKg() {
18
        return pricePerKg;
19
    }
20
    
21
    @Override
22
    public String getName() {
23
        return this.name;
24
    }
25
26
    @Override
27
    public double getPrice() {
28
        return getPricePerKg();
29
    }
30
    
31
    @Override
32
    public double getWeight() {
33
        return weight;
34
    }
35
36
    @Override
37
    public ShopItemCategory getCategory() {
38
        return ShopItemCategory.FRUIT;
39
    }
40
    
41
    @Override
42
    public double accept(ShopVisitor visitor) {
43
        return visitor.visit(this);
44
    }
45
46
    @Override
47
    public String toString() {
48
        StringBuilder builder = new StringBuilder();
49
        builder.append("Fruit [name=").append(name)
50
            .append(", pricePerKg=").append(pricePerKg)
51
            .append(", weight=").append(weight)
52
            .append("]");
53
        return builder.toString();
54
    }
55
56
    
57
}


Updated code for Vegetable class:

Java
xxxxxxxxxx
1
58
 
1
package org.trishinfotech.visitor.example2.items;
2
3
import org.trishinfotech.visitor.example2.ShopVisitor;
4
5
public class Vegetable implements ShopItem {
6
7
    private String name;
8
    private double pricePerKg;
9
    private double weight;
10
    
11
    public Vegetable(String name, double pricePerKg, double weight) {
12
        this.name = name;
13
        this.pricePerKg = pricePerKg;
14
        this.weight = weight;
15
    }
16
17
    public double getPricePerKg() {
18
        return pricePerKg;
19
    }
20
    
21
    @Override
22
    public String getName() {
23
        return this.name;
24
    }
25
    
26
    @Override
27
    public double getPrice() {
28
        return getPricePerKg();
29
    }
30
    
31
    @Override
32
    public double getWeight() {
33
        return weight;
34
    }
35
    
36
    @Override
37
    public double accept(ShopVisitor visitor) {
38
        return visitor.visit(this);
39
    }
40
    
41
    @Override
42
    public ShopItemCategory getCategory() {
43
        return ShopItemCategory.VEGETABLE;
44
    }
45
46
    @Override
47
    public String toString() {
48
        StringBuilder builder = new StringBuilder();
49
        builder.append("Vegetable [name=").append(name)
50
            .append(", pricePerKg=").append(pricePerKg)
51
            .append(", weight=").append(weight)
52
            .append("]");
53
        return builder.toString();
54
    }
55
56
    
57
}


Now we will define the ShopVisitor interface to create our visit() method for each shop-item.

Java
xxxxxxxxxx
1
26
 
1
package org.trishinfotech.visitor.example2;
2
3
import org.trishinfotech.visitor.example2.items.Book;
4
import org.trishinfotech.visitor.example2.items.Electronics;
5
import org.trishinfotech.visitor.example2.items.Fruit;
6
import org.trishinfotech.visitor.example2.items.Vegetable;
7
8
public interface ShopVisitor {
9
10
    static final double DISCOUNT_NONE = 0.0d;
11
    static final double DISCOUNT_5_PERCENT = 0.05d;
12
    static final double DISCOUNT_7_PERCENT = 0.07d;
13
    static final double DISCOUNT_10_PERCENT = 0.10d;
14
    static final double DISCOUNT_12_PERCENT = 0.12d;
15
    static final double DISCOUNT_15_PERCENT = 0.15d;
16
17
    double visit(Fruit item);
18
19
    double visit(Vegetable item);
20
21
    double visit(Book item);
22
23
    double visit(Electronics item);
24
25
}


Now, to use visitor interface, we will make our Customer class to implement it.

Java
xxxxxxxxxx
1
21
 
1
package org.trishinfotech.visitor.example2;
2
3
import org.trishinfotech.visitor.example2.items.ShopItem;
4
5
public abstract class Customer implements ShopVisitor {
6
7
    protected String name;
8
9
    public Customer(String name) {
10
        super();
11
        this.name = name;
12
    }
13
14
    public double calculateCost(ShopItem item, double price, double discount) {
15
        double weight = item.getWeight();
16
        double cost = (weight == 0.0d) ? price : price * weight;
17
        return (discount == 0.0d) ? cost : (cost - (cost * discount));
18
    }
19
20
}


Updated code for Person class:

Java
xxxxxxxxxx
1
58
 
1
package org.trishinfotech.visitor.example2;
2
3
import org.trishinfotech.visitor.example2.items.Book;
4
import org.trishinfotech.visitor.example2.items.Electronics;
5
import org.trishinfotech.visitor.example2.items.Fruit;
6
import org.trishinfotech.visitor.example2.items.Vegetable;
7
8
public class Person extends Customer {
9
10
    public Person(String name) {
11
        super(name);
12
    }
13
14
    @Override
15
    public String toString() {
16
        return "Person Visitor (" + name + ")";
17
    }
18
19
20
    @Override
21
    public double visit(Fruit item) {
22
        double discount = DISCOUNT_NONE;
23
        double weight = item.getWeight();
24
        if (weight >= 2.0d) { // 5% discount if buy more then 2 kg fruit item
25
            discount = DISCOUNT_5_PERCENT;
26
        }
27
        return discount;
28
    }
29
30
    @Override
31
    public double visit(Vegetable item) {
32
        double discount = DISCOUNT_NONE;
33
        double weight = item.getWeight();
34
        if (weight >= 2.0d) { // 10% discount if buy more the 2 kg vegetable item
35
            discount = DISCOUNT_10_PERCENT;
36
        }
37
        return discount;
38
    }
39
40
    @Override
41
    public double visit(Book item) {
42
        return DISCOUNT_NONE; // no discount on books
43
    }
44
45
    @Override
46
    public double visit(Electronics item) {
47
        double discount = DISCOUNT_NONE;
48
        double price = item.getPrice();
49
        if (price >= 500.0d) { // 10% discount if electronic item cost more then 500
50
            discount = DISCOUNT_10_PERCENT;
51
        } else if (price >= 200.0d) { // 5% discount if electronic item cost more then 200
52
            discount = DISCOUNT_5_PERCENT;
53
        }
54
        return discount;
55
    }
56
57
}


Updated code for Student class:

Java
xxxxxxxxxx
1
58
 
1
package org.trishinfotech.visitor.example2;
2
3
import org.trishinfotech.visitor.example2.items.Book;
4
import org.trishinfotech.visitor.example2.items.Electronics;
5
import org.trishinfotech.visitor.example2.items.Fruit;
6
import org.trishinfotech.visitor.example2.items.Vegetable;
7
8
public class Student extends Customer {
9
10
    public Student(String name) {
11
        super(name);
12
    }
13
14
    @Override
15
    public String toString() {
16
        return "Student Visitor (" + name + ")";
17
    }
18
19
    @Override
20
    public double visit(Fruit item) {
21
        double discount = DISCOUNT_NONE;
22
        double weight = item.getWeight();
23
        if (weight >= 2.0d) { // 7% discount if buy more then 2 kg fruit item
24
            discount = DISCOUNT_7_PERCENT;
25
        }
26
        return discount;
27
    }
28
29
    @Override
30
    public double visit(Vegetable item) {
31
        double discount = DISCOUNT_NONE;
32
        double weight = item.getWeight();
33
        if (weight >= 2.0d) { // 12% discount if buy more the 2 kg vegetable item
34
            discount = DISCOUNT_12_PERCENT;
35
        }
36
        return discount;
37
    }
38
39
    @Override
40
    public double visit(Book item) {
41
        // 15% flat discount on all book/magazine/stationary item 
42
        return DISCOUNT_15_PERCENT;
43
    }
44
45
    @Override
46
    public double visit(Electronics item) {
47
        double discount = DISCOUNT_NONE;
48
        double price = item.getPrice();
49
        if (price >= 500.0d) { // 15% discount if electronic item cost more then 500
50
            discount = DISCOUNT_15_PERCENT;
51
        } else if (price >= 200.0d) { // 10% discount if electronic item cost more then 200
52
            discount = DISCOUNT_10_PERCENT;
53
        }
54
        return discount;
55
    }
56
57
}


So, we can see that each visitor class has its own implementation of discount calculation.

Now, we need to update BillPrinter to accommodate discount values, along with the itemized bill print.

Java
xxxxxxxxxx
1
45
 
1
package org.trishinfotech.visitor.example2.util;
2
3
import java.util.Comparator;
4
import java.util.List;
5
import java.util.concurrent.atomic.DoubleAdder;
6
7
import org.trishinfotech.visitor.example2.Customer;
8
import org.trishinfotech.visitor.example2.items.ShopItem;
9
10
public class BillPrinter {
11
12
    public static double calculateAndPrintBill(Customer customer, List<ShopItem> items) {
13
        double totalCost = printItemisedBill(customer, items);
14
        System.out.printf("Total Cost = %82.2f\n", totalCost);
15
        System.out.println(
16
                "============================================================================================================");
17
        return totalCost;
18
    }
19
20
    public static double printItemisedBill(Customer customer, List<ShopItem> items) {
21
        items.sort(Comparator.comparing(ShopItem::getCategory).thenComparing(ShopItem::getName));
22
        System.out.printf("                                Shopping Items Bill for the %s\n", customer);
23
        System.out.println(
24
                "============================================================================================================");
25
        System.out.printf("%25s | %15s | %10s | %10s | %10s | %10s\n", "Shopping Item", "Category", "Price", "Weight",
26
                "Discount", "Cost");
27
        System.out.println(
28
                "------------------------------------------------------------------------------------------------------------");
29
        DoubleAdder totalCost = new DoubleAdder();
30
        items.stream().forEach(item -> {
31
            String name = item.getName();
32
            double price = item.getPrice();
33
            double weight = item.getWeight();
34
            double discount = item.accept(customer);
35
            double cost = customer.calculateCost(item, price, discount);
36
            totalCost.add(cost);
37
            System.out.printf("%25s | %15s | %10.2f | %10s | %10s | %10.2f\n", name, item.getCategory().getName(),
38
                    price, (weight > 0.0d) ? weight : "-", (discount > 0.0d) ? discount : "-", cost);
39
        });
40
        System.out.println(
41
                "============================================================================================================");
42
        return totalCost.doubleValue();
43
    }
44
}


Now, it's time to write our Main program to execute and test the code:

Java
xxxxxxxxxx
1
119
 
1
package org.trishinfotech.visitor.example2;
2
3
import java.util.ArrayList;
4
import java.util.HashMap;
5
import java.util.List;
6
import java.util.Map;
7
8
import org.trishinfotech.visitor.example2.items.Book;
9
import org.trishinfotech.visitor.example2.items.Electronics;
10
import org.trishinfotech.visitor.example2.items.Fruit;
11
import org.trishinfotech.visitor.example2.items.ShopItem;
12
import org.trishinfotech.visitor.example2.items.ShopItemCategory;
13
import org.trishinfotech.visitor.example2.items.Vegetable;
14
import org.trishinfotech.visitor.example2.util.BillPrinter;
15
16
@SuppressWarnings("serial")
17
public class Main {
18
19
    protected static Map<ShopItemCategory, Map<String, Double>> shopItemsPriceList = new HashMap<ShopItemCategory, Map<String, Double>>();
20
21
    static {
22
        shopItemsPriceList.put(ShopItemCategory.BOOK, new HashMap<String, Double>() {
23
            {
24
                put("Java - Design Patterns", 10.4d);
25
                put("Learn java in 24 days", 8.64d);
26
                put("William Shakespeare", 12.43d);
27
                put("Times Magazine", 8.76d);
28
            }
29
        });
30
        shopItemsPriceList.put(ShopItemCategory.FRUIT, new HashMap<String, Double>() {
31
            {
32
                put("Banana", 2.0d);
33
                put("Apple", 3.5d);
34
                put("Mango", 5.5d);
35
                put("Grape", 4.3d);
36
            }
37
        });
38
        shopItemsPriceList.put(ShopItemCategory.VEGETABLE, new HashMap<String, Double>() {
39
            {
40
                put("Potato", 5.0d);
41
                put("Tomato", 1.4d);
42
                put("Onion", 2.43d);
43
                put("Capsicum", 3.76d);
44
            }
45
        });
46
        shopItemsPriceList.put(ShopItemCategory.ELECTRONICS, new HashMap<String, Double>() {
47
            {
48
                put("Casio Calculator", 4.3d);
49
                put("Samsung Mobile", 410.65d);
50
                put("iPhone X", 650.36d);
51
                put("MacBook Pro", 3500.45d);
52
            }
53
        });
54
    }
55
56
    public static void main(String[] args) {
57
        Student student = new Student("Racheal"); // Visitor #1
58
        List<ShopItem> studentItems = prepareShopItems(new HashMap<String, Double>() {
59
            {
60
                put("Java - Design Patterns", 1.0d);
61
                put("Learn java in 24 days", 8.64d);
62
                put("Banana", 2.3d);
63
                put("Apple", 1.4d);
64
                put("Potato", 2.0d);
65
                put("Tomato", 1.5d);
66
                put("Casio Calculator", 1.0d);
67
                put("iPhone X", 1.0d);
68
            }
69
        });
70
        double totalCostForStudent = BillPrinter.calculateAndPrintBill(student, studentItems);
71
        System.out.printf("Amount to pay = %10.2f\n", totalCostForStudent);
72
73
        System.out.println("\n\n");
74
75
        Person person = new Person("Micheal"); // Visitor #2
76
        List<ShopItem> personItems = prepareShopItems(new HashMap<String, Double>() {
77
            {
78
                put("Times Magazine", 1.0d);
79
                put("William Shakespeare", 1.0d);
80
                put("Banana", 2.8d);
81
                put("Mango", 1.4d);
82
                put("Potato", 2.8d);
83
                put("Tomato", 2.5d);
84
                put("Samsung Mobile", 1.0d);
85
                put("MacBook Pro", 1.0d);
86
            }
87
        });
88
        double totalCostForPerson = BillPrinter.calculateAndPrintBill(person, personItems);
89
        System.out.printf("Amount to pay = %10.2f\n", totalCostForPerson);
90
    }
91
92
    private static List<ShopItem> prepareShopItems(HashMap<String, Double> hashMap) {
93
        List<ShopItem> shopItems = new ArrayList<ShopItem>();
94
        hashMap.forEach((item, quantity) -> {
95
            for (ShopItemCategory category : ShopItemCategory.values()) {
96
                Double price = shopItemsPriceList.get(category).get(item);
97
                if (price != null) {
98
                    switch (category) {
99
                    case BOOK:
100
                        shopItems.add(new Book(item, price));
101
                        break;
102
                    case FRUIT:
103
                        shopItems.add(new Fruit(item, price, quantity));
104
                        break;
105
                    case VEGETABLE:
106
                        shopItems.add(new Vegetable(item, price, quantity));
107
                        break;
108
                    case ELECTRONICS:
109
                        shopItems.add(new Electronics(item, price));
110
                        break;
111
                    }
112
                    break;
113
                }
114
            }
115
116
        });
117
        return shopItems;
118
    }
119
}


And below is the output of the program:

Java
x
36
 
1
                                Shopping Items Bill for the Student Visitor (Racheal)
2
============================================================================================================
3
            Shopping Item |        Category |      Price |     Weight |   Discount |       Cost
4
------------------------------------------------------------------------------------------------------------
5
   Java - Design Patterns |            Book |      10.40 |          - |       0.15 |       8.84
6
    Learn java in 24 days |            Book |       8.64 |          - |       0.15 |       7.34
7
         Casio Calculator |     Electronics |       4.30 |          - |          - |       4.30
8
                 iPhone X |     Electronics |     650.36 |          - |       0.15 |     552.81
9
                    Apple |           Fruit |       3.50 |        1.4 |          - |       4.90
10
                   Banana |           Fruit |       2.00 |        2.3 |       0.07 |       4.28
11
                   Potato |       Vegetable |       5.00 |        2.0 |       0.12 |       8.80
12
                   Tomato |       Vegetable |       1.40 |        1.5 |          - |       2.10
13
============================================================================================================
14
Total Cost =                                                                             593.37
15
============================================================================================================
16
Amount to pay =     593.37
17
18
19
20
                                Shopping Items Bill for the Person Visitor (Micheal)
21
============================================================================================================
22
            Shopping Item |        Category |      Price |     Weight |   Discount |       Cost
23
------------------------------------------------------------------------------------------------------------
24
           Times Magazine |            Book |       8.76 |          - |          - |       8.76
25
      William Shakespeare |            Book |      12.43 |          - |          - |      12.43
26
              MacBook Pro |     Electronics |    3500.45 |          - |        0.1 |    3150.40
27
           Samsung Mobile |     Electronics |     410.65 |          - |       0.05 |     390.12
28
                   Banana |           Fruit |       2.00 |        2.8 |       0.05 |       5.32
29
                    Mango |           Fruit |       5.50 |        1.4 |          - |       7.70
30
                   Potato |       Vegetable |       5.00 |        2.8 |        0.1 |      12.60
31
                   Tomato |       Vegetable |       1.40 |        2.5 |        0.1 |       3.15
32
============================================================================================================
33
Total Cost =                                                                            3590.48
34
============================================================================================================
35
Amount to pay =    3590.48
36


I hope that you now have a good idea of how to use the Visitor Design Pattern.

Source Code can be found here: Visitor-Design-Pattern-Sample-Code

Liked the article? Please don't forget to press that like button. Happy coding!

Need more articles, please visit my profile: Brijesh Saxena.

Java (programming language) Design Object (computer science)

Opinions expressed by DZone contributors are their own.

Related

  • Object Relational Behavioral Design Patterns in Java
  • Distribution Design Patterns in Java - Data Transfer Object (DTO) And Remote Facade Design Patterns
  • Context Object Design Pattern in Java: Introduction and Key Points
  • Java: Object Pool Design Pattern

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!