Visitor design pattern example tutorial

Design patterns are used to solve the problems which occur in a pattern, we all know that, right? Also we know that behavioral design patterns are design patterns that identify common communication patterns between objects. One of such behavioral patterns is visitor pattern, which we are going to learn about in this post.

Ads by Google

If you have been in working on a application which manage plenty of products, then you can easily relate with this problem:

“You need to add a new method to a hierarchy of classes, but the act of adding it might be painful or damaging to the design.”

So clearly, you want a hierarchy of objects to modify their behavior but without modifying their source code. How to do that? To solve this problem, visitor pattern comes into picture.

Sections in this post:

Visitor pattern introduction
Design participants/components
Sample problem to solve
Proposed solution using Visitor pattern
Implementation code
How to use visitors in application code
Sourcecode download

Visitor pattern introduction

According to Wikipedia, the visitor design pattern is 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 those structures. It is one way to follow the open/closed principle (one of SOLID design principles).

Above design flexibility allows to add methods to any object hierarchy without modifying the code written for hierarchy. Rather double dispatch mechanism is used to implement this facility. Double dispatch is a special mechanism that dispatches a function call to different concrete functions depending on the runtime types of two objects involved in the call.

Design participants/components

The participants classes in this pattern are:

Visitor -- This is an interface or an abstract class used to declare the visit operations for all the types of visitable classes.

ConcreteVisitor -- For each type of visitor all the visit methods, declared in abstract visitor, must be implemented. Each Visitor will be responsible for different operations.

Visitable -- is an interface which declares the accept operation. This is the entry point which enables an object to be “visited” by the visitor object.

ConcreteVisitable -- Those classes implements the Visitable interface or class and defines the accept operation. The visitor object is passed to this object using the accept operation.

Sample problem to solve

An example is always better than long theory. So let’s have one here also for visitor design pattern.

Suppose we have an application which manage routers in different environments. Routers should be capable of sending and receiving char data from other nodes and application should be capable of configuring routers in different environment.

Essentially, design should flexible enough to support the changes in way that routers can be configured for additional environments in future, without much modifications in source code.

Existing routers in application

Existing routers in application

We have above 3 types of routers and we need to write code for them. One way to solve this problem, is to define methods like configureForWinodws() and configureForLinux() in Router.java interface and implement them in different products, cause each will have it’s own configuration setting and procedure.

But the problem with above approach is that each time a new environment is introduced, whole router’s hierarchy will have to be compiled again. Not acceptable solution. So what is it which can prevent this situation?

Proposed solution using Visitor pattern

Visitor pattern is good fit for these types of problems where you want to introduce a new operation to hierarchy of objects, without changing its structure or modifying them. In this solution, we will implement double dispatch technique by introducing two methods i.e. accept() and visit() methods. Method accept(), will be defined in router’s hierarchy and and visit methods will be on visitors level.

Whenever a new environment need to be added, a new visitor will be added into visitors hierarchy and that needs to implement visit() method for all the available routers and that’s all.

Solution using visitor pattern

Solution using visitor pattern

In above class diagram, we have routers configured for Mac and Linux operating systems. If we need to add the capability for windows also, then I do not need to change any class, just define a new visitor WindowsConfigurator and implement the visit() methods defined in RouterVisitor interface. It will provide the desired functionality without any further modification.

Implementation code

Let’s look at the source code of different files involved into above discussed problem and solution.

Router.java

public interface Router 
{
	public void sendData(char[] data);
	public void acceptData(char[] data);
	
	public void accept(RouterVisitor v);
}

DLinkRouter.java

public class DLinkRouter implements Router{

	@Override
	public void sendData(char[] data) {
	}

	@Override
	public void acceptData(char[] data) {
	}

	@Override
	public void accept(RouterVisitor v) {
		v.visit(this);
	}
}

LinkSysRouter.java

public class LinkSysRouter implements Router{

	@Override
	public void sendData(char[] data) {
	}

	@Override
	public void acceptData(char[] data) {
	}
	
	@Override
	public void accept(RouterVisitor v) {
		v.visit(this);
	}
}

TPLinkRouter.java

public class TPLinkRouter implements Router{

	@Override
	public void sendData(char[] data) {
	}

	@Override
	public void acceptData(char[] data) {
	}
	
	@Override
	public void accept(RouterVisitor v) {
		v.visit(this);
	}
}

RouterVisitor.java

public interface RouterVisitor {
	public void visit(DLinkRouter router);
	public void visit(TPLinkRouter router);
	public void visit(LinkSysRouter router);
}

LinuxConfigurator.java

public class LinuxConfigurator implements RouterVisitor{

	@Override
	public void visit(DLinkRouter router) {
		System.out.println("DLinkRouter Configuration for Linux complete !!");
	}

	@Override
	public void visit(TPLinkRouter router) {
		System.out.println("TPLinkRouter Configuration for Linux complete !!");
	}

	@Override
	public void visit(LinkSysRouter router) {
		System.out.println("LinkSysRouter Configuration for Linux complete !!");
	}
}

MacConfigurator.java

public class MacConfigurator implements RouterVisitor{

	@Override
	public void visit(DLinkRouter router) {
		System.out.println("DLinkRouter Configuration for Mac complete !!");
	}

	@Override
	public void visit(TPLinkRouter router) {
		System.out.println("TPLinkRouter Configuration for Mac complete !!");
	}

	@Override
	public void visit(LinkSysRouter router) {
		System.out.println("LinkSysRouter Configuration for Mac complete !!");
	}
}

How to use visitors in application code

To use above design, use the visitors in below given way. I have used the code in form of JUNIT testcases, you can change the code the way you feel correct into your case.

TestVisitorPattern.java

public class TestVisitorPattern extends TestCase
{
	private MacConfigurator macConfigurator;
	private LinuxConfigurator linuxConfigurator;
	private DLinkRouter dlink;
	private TPLinkRouter tplink;
	private LinkSysRouter linksys;
	
	public void setUp()
	{
		macConfigurator = new MacConfigurator();
		linuxConfigurator = new LinuxConfigurator();
		
		dlink = new DLinkRouter();
		tplink = new TPLinkRouter();
		linksys = new LinkSysRouter();
	}
	
	public void testDlink()
	{
		dlink.accept(macConfigurator);
		dlink.accept(linuxConfigurator);
	}
	
	public void testTPLink()
	{
		tplink.accept(macConfigurator);
		tplink.accept(linuxConfigurator);
	}
	
	public void testLinkSys()
	{
		linksys.accept(macConfigurator);
		linksys.accept(linuxConfigurator);
	}
}

Output:

DLinkRouter Configuration for Mac complete !!
DLinkRouter Configuration for Linux complete !!
LinkSysRouter Configuration for Mac complete !!
LinkSysRouter Configuration for Linux complete !!
TPLinkRouter Configuration for Mac complete !!
TPLinkRouter Configuration for Linux complete !!

Sourcecode download

To download the sourcecode of above application, follow given link.

Happy Learning !!

10 thoughts on “Visitor design pattern example tutorial”

  1. RouterVisitor should take Router interface as dependency. You are using the concrete Router classes which violates the SOLID DI principle..

    so RouterVisitor should have only 1 method visit(Router router)

    In Each Concrete Configurator class, we can implement visit method with Reflection (for all RouterVisitor types)

  2. If we introduced new router, again we need to modify all the visitor right since visit() method is getting Visitable class not interface. Again this will make big issue in OPEN/CLOSED principle. why should not we use interface and print the message based on the object type. please correct me if i m wrong.

    1. Yes you are right. New router will have to be configured for each environment and it will change lots of classes, but isn’t it how it should be done?? It forces the developer to handle new router in each supported environment.

  3. As per definition if we modify structure of any object that should not effect on others…in your example let i need to add one new method in DLinkRouter class then how we are going to handle that…?

Note:- In comment box, please put your code inside [java] ... [/java] OR [xml] ... [/xml] tags otherwise it may not appear as intended.

Leave a Reply

Your email address will not be published. Required fields are marked *


6 + five =

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>