Skip to main content

Visitor Design Pattern


Summary 

Visitor Design pattern follows the open/closed principle and allows us to add new operations to be added to object structure. Open/Closed principle states that entities should be able to extend its behavior or add need new functionality without changing its own implementation.

Important Points

Visitor Design Pattern uses Double Dispatch to invoke a method at run time based on the object on which method is defined and its parameter.
Visitor represents operations or functionality to be performed on different nodes of an object structure.
Visitor Pattern  follows Open/Closed Principle.
Useful in adding new behavior or functionality to a object tree, without modifying the original Objects code.


Advantage 

We can add new functionality to the Hierarchy of Classes without changing the classes.

Disadvantage

The arguments visiting methods have to be known in advance.

Details


The participants classes in this pattern are:

Visitor (visit)-- 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 (accept)-- 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.

Simple Use Case

This example is about  Bookmarks in Browsers. Lets assume we want to process all the bookmarks stored in all browsers and process them.

We will consider that we can have many book marks in a Browser, similarly we can have different Browsers like Firefox, Chrome, IE etc in our workstation or laptop.

Our Model Classes will represent  Workstation, Browsers and Bookmarks etc.

Let's say at some point we want to grab all the bookmarks in a Workstation across all different browsers and print them.

We can declare a Visitor (Say PrintAllBookmarks etc) and propagate through the Object chain to print them.

We can also create a different  Visitor( say Remove bookmark) which will go though the Object structure and remove bookmarks as per some condition.


Class Diagram




Sample Code 



/** Visitor Interface **/
public interface Visitor {
 void visit(WorkStation workstation);
}


/**
 * This Class represent a Workstation Object.
 * Workstation can have different Browsers installed on it.
 *
 */
public class WorkStation {

 private String name;
 
 List browsers = new ArrayList();

 public void accept(Visitor visitor) {
        visitor.visit(this);
 }
 
 
 //GETTERS and SETTERS.....
 public String getWorkstationName() {
  return name;
 }

 public void setWorkstationName(String workstationName) {
  this.name = workstationName;
 }

 public List getBrowsers() {
  return browsers;
 }

 public void addBrowser(Browser browser) {
  this.browsers.add(browser);
 }

}

/**
 * Represents a Browser Object.
 *
 */
public class Browser {
 
 private String name;
 
 List bookmarks = new ArrayList ();
 
 public Browser(String name){
  this.name = name;
 }
 
 
 //GETTERS and SETTERS.....
 public String getBrowserName() {
  return name;
 }

 public List getBookmarks() {
  return bookmarks;
 }
 
 public void addBookmark(String bookmark) {
  this.bookmarks.add(bookmark);
 }
}


/**
 * Concrete visitor implementation. It visits workstation object to read all
 * Browsers and bookmarks and returns them.
 *
 */
public class PrintNodeValueVisitor implements Visitor {
 
 StringBuilder nodeValue = new StringBuilder();

 @Override
 public void visit(WorkStation workstation) {
  StringBuilder str = new StringBuilder();
  str.append("{")
  .append("WorkStationName:").append(workstation.getWorkstationName())
  .append("{");
   str.append("Browsers [");
  for(Browser browser: workstation.getBrowsers()){
   str.append("BrowserName:").append(browser.getBrowserName());
     str.append("BookMarkDetails [");
    for(NameValuePair bookmark : browser.getBookmarks()){
     str.append("BookMarkName:");
     str. append(bookmark.name);
     str. append(",");
     str.append("BookMarkURL:");
     str.append(bookmark.value);
    }
    str.append("]");
  }
   str.append("]");
  str.append("}");
  str.append("}");
  
  nodeValue = str;
  
 }
}

Full source code can be found on github here 
https://github.com/siddharthagit/Sidd_javaprojDP/tree/master/src/dp/behavioral/visitor

Comments

Popular posts from this blog

Converting Java Map to String

Java Collections framework, String manipulation etc is something that we often encounter in Development process.
For processing collections (like checking null/empty, Intersection, Disjunction) We do have some of the very use full libraries.

Some of the Collection related libraries are Apche Commons Collections and Google  Collections(Guava).

Problem Use Case

This article explains how to convert a Java Map to String(and vice versa) using different libraries and technique.

One way is to use StringBuilder(Or String) and loop though the Map and build the String by applying some sort of separator ( for key:value and entry). Here we have to take care of the null value etc.

Without Any Library
If we want to convert the map to a String with key value separator and also individual entry seperator in the resulting String, we have to write code for that. For a simple Map, we have to iterate though the map, take care of the null values etc. Following is a sample to get String built out from Map C…

Masking Credit Card number in Java

Sometimes we need to mask crucial information like Credit Card Numbers, CVV numbers etc before storing  or logging the information. This example mask Credit Card Number (Except last 4 Digit) from a Text which contains information along with Credit Card Number.

The following example demonstrates how we can easily mask the credit card with Matcher and Pattern Classes. This Sample Code uses Matcher and Pattern.
Pattern Used in this sample is not optimized for Credit Card Numbers, this pattern will get any numerical numbers in the String Content.  Based on the Credit Card Type a more efficient and Strict RegEx can be used to mask the Credit Card.
/**Mask the Credit card number but last four digit value **/   Pattern PATTERN = Pattern.compile("[0-9]+"); String message = content; Matcher matcher = PATTERN.matcher(message); String maskingChar = "*"; StringBuilder finalMask = new StringBuilder(maskingChar); while …

Invoking EJB deployed on a remote machine

Invoking EJB deployed on a remote machineIn case we are calling remote ejb( ejb deployed on remote machines),The JNDI lookup might lookup like,Properties env = new Properties();env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");env.put(Context.PROVIDER_URL, "XX.XXX.XX.XX:1099");env.put("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces"); Context ctx = new InitialContext(env);If we are calling local ejb then we can simply create InitialContext without any parameters.Like,Context ctx = new InitialContext();