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.
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);
}
}