📄 pat5k.htm
字号:
<HTML><HEAD><TITLE>Visitor</TITLE><SCRIPT>function setFocus() { if ((navigator.appName != "Netscape") && (parseFloat(navigator.appVersion) == 2)) { return; } else { self.focus(); }}</SCRIPT></HEAD><BODY BGCOLOR = #FFFFFF onLoad="setFocus()";><A NAME="top"></A><A NAME="Visitor"></A><A NAME="intent"></A><H2><A HREF="#motivation"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Motivation"></A> Intent</H2> <A NAME="auto1000"></A><P>Represent an operation to be performed on the elements of an objectstructure. Visitor lets you define a new operation without changing theclasses of the elements on which it operates.</P><A NAME="motivation"></A><H2><A HREF="#applicability"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Applicability"></A> Motivation</H2> <A NAME="abssyntree"></A><P>Consider a compiler that represents programs as abstract syntax trees.It will need to perform operations on abstract syntax trees for "staticsemantic" analyses like checking that all variables are defined. Itwill also need to generate code. So it might define operations fortype-checking, code optimization, flow analysis, checking for variablesbeing assigned values before they're used, and so on. Moreover, we coulduse the abstract syntax trees for pretty-printing, programrestructuring, code instrumentation, and computing various metrics of aprogram.</P><A NAME="auto1001"></A><P>Most of these operations will need to treat nodes that representassignment statements differently from nodes that represent variables orarithmetic expressions. Hence there will be one class for assignmentstatements, another for variable accesses, another for arithmeticexpressions, and so on. The set of node classes depends on the languagebeing compiled, of course, but it doesn't change much for a givenlanguage.</P><A NAME="abssync"></A><P ALIGN=CENTER><IMG SRC="Pictures/visit006.gif"></P><A NAME="auto1002"></A><P>This diagram shows part of the Node class hierarchy. The problem hereis that distributing all these operations across the various nodeclasses leads to a system that's hard to understand, maintain, andchange. It will be confusing to have type-checking code mixed withpretty-printing code or flow analysis code. Moreover, adding a newoperation usually requires recompiling all of these classes. It would bebetter if each new operation could be added separately, and the nodeclasses were independent of the operations that apply to them.</P><A NAME="def-visitor"></A><P>We can have both by packaging related operations from each class in aseparate object, called a <STRONG>visitor</STRONG>, and passing it toelements of the abstract syntax tree as it's traversed. When an element"accepts" the visitor, it sends a request to the visitor that encodesthe element's class. It also includes the element as an argument. Thevisitor will then execute the operation for that element—theoperation that used to be in the class of the element.</P><A NAME="typecheck"></A><P>For example, a compiler that didn't use visitors might type-check aprocedure by calling the TypeCheck operation on its abstract syntaxtree. Each of the nodes would implement TypeCheck by calling TypeCheckon its components (see the preceding class diagram). If the compilertype-checked a procedure using visitors, then it would create aTypeCheckingVisitor object and call the Accept operation on theabstract syntax tree with that object as an argument. Each of thenodes would implement Accept by calling back on the visitor: anassignment node calls VisitAssignment operation on the visitor, whilea variable reference calls VisitVariableReference. What used to be theTypeCheck operation in class AssignmentNode is now the VisitAssignmentoperation on TypeCheckingVisitor.</P><A NAME="auto1003"></A><P>To make visitors work for more than just type-checking, we need anabstract parent class NodeVisitor for all visitors of an abstract syntaxtree. NodeVisitor must declare an operation for each node class. Anapplication that needs to compute program metrics will define newsubclasses of NodeVisitor and will no longer need to addapplication-specific code to the node classes. The Visitor patternencapsulates the operations for each compilation phase in a Visitorassociated with that phase.</P><A NAME="332c"></A><P ALIGN=CENTER><IMG SRC="Pictures/visit113.gif"></P><A NAME="333c"></A><P ALIGN=CENTER><IMG SRC="Pictures/visit112.gif"></P><A NAME="auto1004"></A><P>With the Visitor pattern, you define two class hierarchies: one for theelements being operated on (the Node hierarchy) and one for the visitorsthat define operations on the elements (the NodeVisitor hierarchy). Youcreate a new operation by adding a new subclass to the visitor classhierarchy. As long as the grammar that the compiler accepts doesn'tchange (that is, we don't have to add new Node subclasses), we can addnew functionality simply by defining new NodeVisitor subclasses.</P><A NAME="applicability"></A><H2><A HREF="#structure"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Structure"></A> Applicability</H2> <A NAME="auto1005"></A><P>Use the Visitor pattern when</P><UL><A NAME="auto1006"></A><LI>an object structure contains many classes of objects with differinginterfaces, and you want to perform operations on these objects thatdepend on their concrete classes.</LI><A NAME="auto1007"></A><P></P><A NAME="auto1008"></A><LI>many distinct and unrelated operations need to be performed on objectsin an object structure, and you want to avoid "polluting" theirclasses with these operations. Visitor lets you keep related operationstogether by defining them in one class. When the object structure isshared by many applications, use Visitor to put operations in just thoseapplications that need them.</LI><A NAME="auto1009"></A><P></P><A NAME="auto1010"></A><LI>the classes defining the object structure rarely change, but you oftenwant to define new operations over the structure. Changing the objectstructure classes requires redefining the interface to all visitors,which is potentially costly. If the object structure classes changeoften, then it's probably better to define the operations in thoseclasses.</LI></UL><A NAME="structure"></A><H2><A HREF="#participants"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Participants"></A> Structure</H2> <P ALIGN=CENTER><IMG SRC="Pictures/visitor.gif"></P><A NAME="participants"></A><H2><A HREF="#collaborations"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Collaborations"></A> Participants</H2><UL><A NAME="auto1011"></A><LI><B>Visitor</B> (NodeVisitor)</LI><A NAME="auto1012"></A><P></P> <UL> <A NAME="auto1013"></A><LI>declares a Visit operation for each class of ConcreteElement in the object structure. The operation's name and signature identifies the class that sends the Visit request to the visitor. That lets the visitor determine the concrete class of the element being visited. Then the visitor can access the element directly through its particular interface.</LI> </UL><A NAME="auto1014"></A><P></P><A NAME="auto1015"></A><LI><B>ConcreteVisitor</B> (TypeCheckingVisitor)</LI><A NAME="auto1016"></A><P></P> <UL> <A NAME="auto1017"></A><LI>implements each operation declared by Visitor. Each operation implements a fragment of the algorithm defined for the corresponding class of object in the structure. ConcreteVisitor provides the context for the algorithm and stores its local state. This state often accumulates results during the traversal of the structure.</LI> </UL><A NAME="auto1018"></A><P></P><A NAME="part-element"></A><LI><B>Element</B> (Node)</LI><A NAME="auto1019"></A><P></P> <UL> <A NAME="auto1020"></A><LI>defines an Accept operation that takes a visitor as an argument.</LI> </UL><A NAME="auto1021"></A><P></P><A NAME="auto1022"></A><LI><B>ConcreteElement</B> (AssignmentNode,VariableRefNode)</LI><A NAME="auto1023"></A><P></P> <UL> <A NAME="auto1024"></A><LI>implements an Accept operation that takes a visitor as an argument.</LI> </UL><A NAME="auto1025"></A><P></P><A NAME="auto1026"></A><LI><B>ObjectStructure</B> (Program)</LI><A NAME="auto1027"></A><P></P> <UL> <A NAME="auto1028"></A><LI>can enumerate its elements.</LI> <A NAME="auto1029"></A><P><!-- extra space --></P> <A NAME="auto1030"></A><LI>may provide a high-level interface to allow the visitor to visit its elements.</LI> <A NAME="auto1031"></A><P><!-- extra space --></P> <A NAME="auto1032"></A><LI>may either be a composite (see <A HREF="pat4cfs.htm" TARGET="_mainDisplayFrame">Composite (163)</A>) or a collection such as a list or a set.</LI> </UL></UL><A NAME="collaborations"></A><H2><A HREF="#consequences"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Consequences"></A> Collaborations</H2><UL><A NAME="auto1033"></A><LI>A client that uses the Visitor pattern must create a ConcreteVisitorobject and then traverse the object structure, visiting each elementwith the visitor.</LI><A NAME="auto1034"></A><P></P><A NAME="auto1035"></A><LI>When an element is visited, it calls the Visitor operation thatcorresponds to its class. The element supplies itself as an argumentto this operation to let the visitor access its state, if necessary.<A NAME="auto1036"></A><P>The following interaction diagram illustrates the collaborationsbetween an object structure, a visitor, and two elements:</P><P ALIGN=CENTER><IMG SRC="Pictures/visit003.gif"></P></LI></UL><A NAME="consequences"></A><H2><A HREF="#implementation"><IMG SRC="gifsb/down3.gif" BORDER=0 ALT="next: Implementation"></A> Consequences</H2> <A NAME="auto1037"></A><P>Some of the benefits and liabilities of the Visitor pattern are as follows:</P><OL><A NAME="auto1038"></A><LI><EM>Visitor makes adding new operations easy.</EM>Visitors make it easy to add operations that depend on the components ofcomplex objects. You can define a new operation over an object structuresimply by adding a new visitor. In contrast, if you spread functionalityover many classes, then you must change each class to define a newoperation.</LI><A NAME="auto1039"></A><P></P><A NAME="auto1040"></A><LI><EM>A visitor gathers related operations and separates unrelated ones.</EM>Related behavior isn't spread over the classes defining the objectstructure; it's localized in a visitor. Unrelated sets of behavior arepartitioned in their own visitor subclasses. That simplifies both theclasses defining the elements and the algorithms defined in thevisitors. Any algorithm-specific data structures can be hidden in thevisitor.</LI><A NAME="auto1041"></A><P></P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -