Visitor¶
The Visitor
pattern solves the problem of many complex algorithms that an application uses. Its purpose is to separate the algorithm definitions from the objects on which they can be used.
Visitor
it is a rarely used pattern due to its complexity level.
Desing¶
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 interfaces
- interface representing the
Visitor
, which has the ability to "visit" individual implementations of elements (usually a method per implementation), and its implementation.
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
In addition, the example implements the Visitor
interface, whose implementation of HTMLFileValidator
has the ability to perform HTML file syntax validation.
class HTMLFile:
def get_doctype_declaration(self):
pass
def get_head(self):
pass
def get_body(self):
pass
def accept(self, visitor):
pass
class HTML4File(HTMLFile):
def __init__(self, head, body, visitor):
self._head = head
self._body = body
self._visitor = visitor
def get_doctype_declaration(self):
return ('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"\n'
' "http://www.w3.org/TR/html4/loose.dtd">')
def get_head(self):
return self._head
def get_body(self):
return self._body
def accept(self, visitor):
visitor.validate_html4_file(self)
class HTML5File(HTMLFile):
def __init__(self, head, body, visitor):
self._head = head
self._body = body
self._visitor = visitor
def get_doctype_declaration(self):
return '<!DOCTYPE html>'
def get_head(self):
return self._head
def get_body(self):
return self._body
def accept(self, visitor):
visitor.validate_html5_file(self)
class XHTMLFile(HTMLFile):
def __init__(self, head, body, visitor):
self._head = head
self._body = body
self._visitor = visitor
def get_doctype_declaration(self):
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">')
def get_head(self):
return self._head
def get_body(self):
return self._body
def accept(self, visitor):
visitor.validate_xhtml_file(self)
class Visitor:
def validate_html4_file(self, html4_file):
pass
def validate_html5_file(self, html5_file):
pass
def validate_xhtml_file(self, xhtml_file):
pass
The implementation of the Visitor
interface only simulates the execution of validation:
class HTMLFileValidator(Visitor):
def validate_html4_file(self, html4_file):
print("Validating HTML 4 schema with https://validator.w3.org/#validate_by_uri+with_options")
def validate_html5_file(self, html5_file):
print("Validating HTML 5 schema with https://validator.w3.org/#validate_by_uri+with_options")
def validate_xhtml_file(self, xhtml_file):
print("Validating XHTML schema with https://validator.w3.org/#validate_by_uri+with_options")
def main():
visitor = HTMLFileValidator()
html4_file = HTML4File('<head>\n <title>Title</title>\n</head>','<body>\n<p>HTML4 FILE</p>\n</body>', visitor)
html5_file = HTML5File('<head>\n <meta charset="UTF-8">\n <title>Title</title>\n</head>'
, '<body>\n<p>HTML5 FILE</p>\n</body>', visitor)
xhtml_file = XHTMLFile('<head>\n <title>Title</title>\n</head>', '<body>\n<p>XHTML file</p>\n</body>', visitor)
html4_file.accept(visitor)
html5_file.accept(visitor)
xhtml_file.accept(visitor)
if __name__ == '__main__':
main()