visitor pattern java real world example

Visitor design pattern will be usefull in applications where we do not know all possible use cases during design time . There might be request for new features from time to time, and in order to implement them, some refactoring has to be done. In these cases this pattern helps us to add new operations to existing object structures without modifying them.

This pattern allows us to keep our structures separately and then use the visitor design pattern to add functionality on top of out existing structure. Visitor pattern is also usefull in cases where we have a object structure where we have a base class which has all the operations defined and only a few of them are implemented by the concrete classes here we can create visitors that will add the functionality when required instead of defining all the operations in the base class.

The visitor design pattern is usefull when we have large object hierarchies, where adding a new functionality will involve a lot of code re factoring.

Below is the generic class diagram for visitor pattern

Lets take an example

We have a system which produced the dynamic line management data and system data for all the communication providers like airtel,idea and vadofone. We have a upstream applications which produces this data and puts all the data of dynamic line management inside one folder and the system data is another folder structure. The file naming convention will have the required information to distinguish the communication provider for example if a file belongs to airtel the file name convention will be Airtel_(.*).tar.gz. So now the requirement is to send these data into the appropriate communication provider. As we are expecting new communication providers to register into this service and new data like call details data would be required in future we need to make sure we should be able to add these features without making any changes into the existing code.

To solve this problem lets use visitor design pattern

Below is the class diagram we want to achieve finally

Lets code the required interfaces first


public interface Visitor {

public void sendFiles(Vadafone vadafone);

public void sendFiles(Airtel airtel);

public void sendFiles(Idea idea);

}


public interface CommunicationProvider {

public void accept(Visitor visitor);

}

Lets add the concrete visitor implementation DynamicLineDataVisitor and the SystemDataVisitor


import java.io.File;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DynamicLineDataVisitor implements Visitor {

public List<File> listOfFile;

public DynamicLineDataVisitor(List<File> listOfFile) {
super();
this.listOfFile = listOfFile;
}

@Override
public void sendFiles(Vadafone vadafone) {
// TODO Auto-generated method stub

Pattern pattern=vadafone.getPattern();
for (File file : listOfFile) {

Matcher matcher = pattern.matcher(file.getName());
if (matcher.find()) {

// Logic to send the dynamic line data into vadofone system

System.out.println("Sending vadofone files");

}

}

}

@Override
public void sendFiles(Airtel airtel) {
// TODO Auto-generated method stub

Pattern pattern=airtel.getPattern();
for (File file : listOfFile) {

Matcher matcher = pattern.matcher(file.getName());
if (matcher.find()) {

// Logic to send the dynamic line data into airtel system
System.out.println("Sending airtel files");

}

}

}

@Override
public void sendFiles(Idea idea) {
// TODO Auto-generated method stub

Pattern pattern=idea.getPattern();
for (File file : listOfFile) {

Matcher matcher = pattern.matcher(file.getName());
if (matcher.find()) {

// Logic to send the dynamic line data into Idea system
System.out.println("Sending Idea files");

}

}

}

}


import java.io.File;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SystemDataVisitor implements Visitor {

public List<File> listOfFile;

public SystemDataVisitor(List<File> listOfFile) {
super();
this.listOfFile = listOfFile;
}

@Override
public void sendFiles(Vadafone vadafone) {
// TODO Auto-generated method stub

Pattern pattern=vadafone.getPattern();
for (File file : listOfFile) {

Matcher matcher = pattern.matcher(file.getName());
if (matcher.find()) {

// Logic to send the SystemData into vadofone system
System.out.println("Sending Vadafone files");

}

}

}

@Override
public void sendFiles(Airtel airtel) {
// TODO Auto-generated method stub

Pattern pattern=airtel.getPattern();
for (File file : listOfFile) {

Matcher matcher = pattern.matcher(file.getName());
if (matcher.find()) {

// Logic to send the SystemData into airtel system

System.out.println("Sending Airtel files");
}

}

}

@Override
public void sendFiles(Idea idea) {
// TODO Auto-generated method stub

Pattern pattern=idea.getPattern();
for (File file : listOfFile) {

Matcher matcher = pattern.matcher(file.getName());
if (matcher.find()) {

// Logic to send the SystemData into Idea system

System.out.println("Sending Idea files");
}

}

}
}

Lets add the concrete communication provider implementation


import java.util.regex.Pattern;

public class Airtel implements CommunicationProvider{

private Pattern pattern = Pattern.compile("Airtel_(.*).tar.gz");

public Pattern getPattern() {
return pattern;
}

public void setPattern(Pattern pattern) {
this.pattern = pattern;
}

@Override
public void accept(Visitor visitor) {

visitor.sendFiles(this);

}

}


import java.util.regex.Pattern;

public class Vadafone implements CommunicationProvider {

private Pattern pattern = Pattern.compile("Vadafone_(.*).tar.gz");

public Pattern getPattern() {
return pattern;
}

public void setPattern(Pattern pattern) {
this.pattern = pattern;
}

@Override
public void accept(Visitor visitor) {

visitor.sendFiles(this);

}

}


import java.util.regex.Pattern;

public class Idea implements CommunicationProvider{

private Pattern pattern = Pattern.compile("Idea_(.*).tar.gz");

public Pattern getPattern() {
return pattern;
}

public void setPattern(Pattern pattern) {
this.pattern = pattern;
}

@Override
public void accept(Visitor visitor) {

visitor.sendFiles(this);

}

}

Finally lets code the test class


import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class Test {

public static void main(String[] args) {

File file1=new File("Airtel_(.*).tar.gz");

File file2=new File("Vadafone_(.*).tar.gz");

File file3=new File("Idea_(.*).tar.gz");

List<File> listOfFiles=new ArrayList<File>();

listOfFiles.add(file1);

listOfFiles.add(file2);

listOfFiles.add(file3);

DynamicLineDataVisitor dynamicLineDataVisitor=new DynamicLineDataVisitor(listOfFiles);

SystemDataVisitor systemDataVisitor=new SystemDataVisitor(listOfFiles);

CommunicationProvider airtel=new Airtel();

CommunicationProvider vadofone=new Vadafone();

CommunicationProvider idea=new Idea();

airtel.accept(dynamicLineDataVisitor);

vadofone.accept(systemDataVisitor);

idea.accept(dynamicLineDataVisitor);

}

}

As we can in the above example we are able to completely decouple the data and the communication providers. So in case in future i want to add a new vendor say aircel i just need to extend the CommunicationProvider interface and implement the requirement methods . Same way if i have to start sending call details information which will be a new data i just need to add a new visitor for call details and implement the required methods from the visitor interface without modifying any existing classes.