Skip to content

Visitor

The Visitor pattern addresses the problem of many complex algorithms that the application uses. Its purpose is to separate the algorithm definitions from the objects on which they can be used.

Visitor is not a very popular pattern due to its complexity level.

Construction

In order to use the Visitor pattern we need to create:

  • a common interface representing the elements on which a certain algorithm can be executed. In addition to methods specific to possible actions, there should be a method to "accept" the Visitor object
  • implementations of the above interface
  • interface representing the Visitor, which has the ability to "visit" individual implementations of elements (usually method per implementation), and its implementation.

visitor

An Example

The example is based on the HTMLFile object and the file validation algorithm.

The HTMLFile interface has a method to "accept" the Visitor and has three implementations:

  • HTML4File
  • HTML5File
  • XHTMLFile

Moreover, the example implements the Visitor interface, the implementation of which HTMLFileValidator has the ability to perform HTML file syntax validation. The VisitorUsage class shows how to use the pattern.

public interface HTMLFile {
  String getDoctypeDeclaration();
  String getHead();
  String getBody();
  void accept(Visitor visitor);
}
public class HTML4File implements HTMLFile {

  private final String head;
  private final String body;
  private final Visitor visitor;

  public HTML4File(final String head, final String body, final Visitor visitor) {
    this.head = head;
    this.body = body;
    this.visitor = visitor;
  }

  @Override
  public String getDoctypeDeclaration() {
    return "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n" +
        "        \"http://www.w3.org/TR/html4/loose.dtd\">";
  }

  @Override
  public String getHead() {
    return head;
  }

  @Override
  public String getBody() {
    return body;
  }

  @Override
  public void accept(final Visitor visitor) {
    visitor.validateFile(this);
  }
}
public class HTML5File implements HTMLFile {

  private final String head;
  private final String body;
  private final Visitor visitor;

  public HTML5File(final String head, final String body, final Visitor visitor) {
    this.head = head;
    this.body = body;
    this.visitor = visitor;
  }

  @Override
  public String getDoctypeDeclaration() {
    return "<!DOCTYPE html>";
  }

  @Override
  public String getHead() {
    return head;
  }

  @Override
  public String getBody() {
    return body;
  }

  @Override
  public void accept(final Visitor visitor) {
    visitor.validateFile(this);
  }
}
public class XHTMLFile implements HTMLFile {

  private final String head;
  private final String body;
  private final Visitor visitor;

  public XHTMLFile(final String head, final String body, final Visitor visitor) {
    this.head = head;
    this.body = body;
    this.visitor = visitor;
  }

  @Override
  public String getDoctypeDeclaration() {
    return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
        "<!DOCTYPE html\n" +
        "        PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" +
        "        \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" +
        "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">";
  }

  @Override
  public String getHead() {
    return head;
  }

  @Override
  public String getBody() {
    return body;
  }

  @Override
  public void accept(final Visitor visitor) {
    visitor.validateFile(this);
  }
}
public interface Visitor {
  void validateFile(HTML4File html4File);
  void validateFile(HTML5File html5File);
  void validateFile(XHTMLFile xhtmlFile);
}

The implementation of the Visitor interface only simulates the execution of validation:

@Slf4j
public class HTMLFileValidator implements Visitor {

  @Override
  public void validateFile(final HTML4File html4File) {
    log.info("Validating HTML 4 schema with https://validator.w3.org/#validate_by_uri+with_options");
  }

  @Override
  public void validateFile(final HTML5File html5File) {
    log.info("Validating HTML 5 schema with https://validator.w3.org/#validate_by_uri+with_options");
  }

  @Override
  public void validateFile(final XHTMLFile xhtmlFile) {
    log.info("Validating XHTML schema with https://validator.w3.org/#validate_by_uri+with_options");
  }
}
public class VisitorUsage {

  public static void main(String[] args) {
    final Visitor visitor = new HTMLFileValidator();

    final HTML4File html4File = new HTML4File("<head>\n" +
        "    <title>Title</title>\n" +
        "</head>","<body>\n" +
        "<p>HTML4 FILE</p>\n" +
        "</body>", visitor);

    final HTML5File html5File = new HTML5File("<head>\n" +
        "    <meta charset=\"UTF-8\">\n" +
        "    <title>Title</title>\n" +
        "</head>", "<body>\n" +
        "<p>HTML5 FILE</p>\n" +
        "</body>",  visitor);

    final XHTMLFile xhtmlFile = new XHTMLFile("<head>\n" +
        "    <title>Title</title>\n" +
        "</head>", "<body>\n" +
        "<p>XHTML file</p>\n" +
        "</body>", visitor);

    visitor.validateFile(html4File);
    visitor.validateFile(html5File);
    visitor.validateFile(xhtmlFile);
  }
}