Creational Patterns ( ์์ฑ ํจํด )
These design patterns
provides way to create objects while hiding the creation logic, rather
than instantiating objects directly using new operator. This gives
program more flexibility in deciding which objects need to be created
for a given use case.
์์ฑํจํด์ ๊ฐ์ฒด์ ์์ฑ๋ก์ง์ ์จ๊ธฐ๊ณ new ๋ช
๋ น์ด๋ฅผ ํตํ์ง ์๊ณ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ๋ค์ ์ ๊ณตํ๋ค. ์ด๋ ํน์ ์ํฉ์์ ์ด๋ค ๋ฐฉ๋ฒ์ผ๋ก ๊ฐ์ฒด๋ฅผ ์์ฑํด์ผํ ์ง๋ฅผ ๊ฒฐ์ ํ๋๋ฐ ์์ด์ ์ ์ฐ์ฑ์ ์ ๊ณตํ๋ค.
Structural Patterns ( ๊ตฌ์กฐ์ ํจํด )
These
design patterns concern class and object composition. Concept of
inheritance is used to compose interfaces and define ways to compose
objects to obtain new functionalities.
๊ตฌ์กฐ์ ํจํด๋ค์ ํด๋์ค์ ๊ฐ์ฒด์ ๊ตฌ์ฑ์ ๊ด์ฌํ๋ค.
Behavioral Patterns ( ํ์์ ํจํด )
These design patterns are specifically concerned with communication between objects.
์ด ํจํด๋ค์ ๊ฐ์ฒด๋ค ์ฌ์ด์ ์ปค๋ฎค๋์ผ์ด์
์ ๊ด์ฌ์ ๊ฐ์ง๋ค.
์ค๋ ์ดํด๋ณผ Visitor ํจํด์ Behavioral ํจํด์ ์ํ๋ค.
Visitor Pattern Structure
ํจํด์ ๋ชฉ์ ( Intent ) :
Represent
an operation to be performed on the elements of an object structure.
Visitor lets you define a new operation without changing the classes of
the elements on which it operates.
ํจํด์ด ๋์ค๊ฒ ๋ ๋๊ธฐ ( Motivation ) :
Consider
a compiler that represents programs as abstract syntax trees. It will
need to perform operations on abstract syntax trees for "static
semantic" analyses like checking that all variables are defined. Itwill
also need to generate code. So it might define operations for
type-checking, code optimization, flow analysis, checking for variables
being assigned values before they're used, and so on.
Moreover, we
could use the abstract syntax trees for pretty-printing, program
restructuring, code instrumentation, and computing various metrics of
aprogram.
Most of these operations will need to treat nodes that
represent assignment statements differently from nodes that represent
variables orarithmetic expressions. Hence there will be one class for
assignment statements, another for variable accesses, another for
arithmetic expressions, and so on. The set of node classes depends on
the language being compiled, of course, but it doesn't change much for a
given language.
This
diagram shows part of the Node class hierarchy. The problem here is
that distributing all these operations across the various node classes
leads to a system that's hard to understand, maintain, and change. It
will be confusing to have type-checking code mixed with pretty-printing
code or flow analysis code. Moreover, adding a new operation usually
requires recompiling all of these classes. It would be better if each
new operation could be added separately, and the node classes were
independent of the operations that apply to them.
We can have both by
packaging related operations from each class in a separate object,
called a visitor, and passing it to elements of the abstract syntax tree
as it's traversed. When an element "accepts" the visitor, it sends a
request to the visitor that encodes the element's class. It also
includes the element as an argument. The visitor will then execute the
operation for that element โthe operation that used to be in the class
of the element.
For example, a compiler that didn't use
visitors might type-check a procedure by calling the TypeCheck operation
on its abstract syntax tree. Each of the nodes would implement
TypeCheck by calling TypeCheck on its components (see the preceding
class diagram). If the compiler type-checked a procedure using visitors,
then it would create a TypeCheckingVisitor object and call the Accept
operation on the abstract syntax tree with that object as an argument.
Each of the nodes would implement Accept by calling back on the visitor:
anassignment node calls
VisitAssignment operation on the visitor,
while a variable reference calls VisitVariableReference. What used to be
the TypeCheck operation in class AssignmentNode is now the
VisitAssignment operation on TypeCheckingVisitor.
To make
visitors work for more than just type-checking, we need an abstract
parent class NodeVisitor for all visitors of an abstract syntax tree.
NodeVisitor must declare an operation for each node class. An
application that needs to compute program metrics will define new
subclasses of NodeVisitor and will no longer need to add
application-specific code to the node classes. The Visitor pattern
encapsulates the operations for each compilation phase in a Visitor
associated with that phase.
With
the Visitor pattern, you define two class hierarchies: one for the
elements being operated on (the Node hierarchy) and one for the visitors
that define operations on the elements (the NodeVisitor hierarchy).
You create a new operation by adding a new subclass to the visitor class
hierarchy. As long as the grammar that the compiler accepts doesn't
change (that is, we don't have to add new Node subclasses), we can add
new functionality simply by defining new NodeVisitor subclasses.
์ ์ฉ์ฑ ( Applicability ) :
Use the Visitor pattern when
Visitor ํจํด์ ์ด๋ด๋ ์ฌ์ฉํ์!!
ยท
an object structure contains many classes of objects with differing
interfaces, and you want to perform operations on these objects that
depend on their concrete classes.
ยท many distinct and unrelated
operations need to be performed on objects in an object structure, and
you want to avoid "polluting" their classes with these operations.
Visitor lets you keep related operations together by defining them in
one class. When the object structure is shared by many applications,
use Visitor to put operations in just those applications that need them.
ยท the classes defining the object structure rarely change, but you often want to define new operations over the structure. Changing the object structure classes requires redefining the interface to all visitors,which is potentially costly. If the object structure classes change often, then it's probably better to define the operations in those classes.
๋ฑ์ฅ ์ธ๋ฌผ ( Participants ) :
ยท Visitor (NodeVisitor)
o
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.
ยท ConcreteVisitor (TypeCheckingVisitor)
o
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.
ยท Element (Node)
o defines an Accept operation that takes a visitor as an argument.
ยท ConcreteElement (AssignmentNode,VariableRefNode)
o implements an Accept operation that takes a visitor as an argument.
ยท ObjectStructure (Program)
o can enumerate its elements.
o may provide a high-level interface to allow the visitor to visit its elements.
o may either be a composite (see Composite ) or a collection such as a list or a set.
์๋ฆฌ ( Collaborations ) :
ยท
A client that uses the Visitor pattern must create a ConcreteVisitor
object and then traverse the object structure, visiting each element
with the visitor.
ยท When an element is visited, it calls the Visitor
operation that corresponds to its class. The element supplies itself as
an argument to this operation to let the visitor access its state, if
necessary.
The following interaction diagram illustrates the collaborations between an object structure, a visitor, and two elements:
ํจํด ์ฌ์ฉ์ ์ฅ๋จ์ ( Consequences ):
Some of the benefits and liabilities of the Visitor pattern are as follows:
1. Visitor makes adding new operations easy.
Visitors make it easy to add operations that depend on the components
ofcomplex objects. You can define a new operation over an object
structure simply by adding a new visitor.
In contrast, if you spread functionality over many classes, then you must change each class to define a newoperation.
2. A visitor gathers related operations and separates unrelated ones.
Related behavior isn't spread over the classes defining the object
structure; it's localized in a visitor. Unrelated sets of behavior are
partitioned in their own visitor subclasses. That simplifies both the
classes defining the elements and the algorithms defined in the
visitors. Any algorithm-specific
data structures can be hidden in thevisitor.
3. Adding new ConcreteElement classes is hard.
The Visitor pattern makes it hard to add new subclasses of Element.
Each new ConcreteElement gives rise to a new abstract operation on
Visitor and a corresponding implementation in every ConcreteVisitor
class. Sometimes a default implementation can be provided in Visitor
that can be inherited by most of the ConcreteVisitors, but this is the
exception rather than the rule. So the key consideration in applying the
Visitor pattern is whether you are mostly likely to change the
algorithm applied over an object structure or the classes of objects
that make up the structure. TheVisitor class hierarchy can be difficult
to maintain when new ConcreteElement classes are added frequently. In
such cases, it's probably easier just to define operations on the
classes that make up the structure. If the Element class hierarchy is
stable, but you are continually adding operations or changing
algorithms, then the Visitor pattern will help you manage the changes.
4. Visiting across class hierarchies.
An iterator (see Iterator ) can visit the objects in a structure as it
traverses them by calling their operations. But an iterator can't work
across object structures with different types of elements.
5. Accumulating state.
Visitors can accumulate state as they visit each element in the object
structure. Without a visitor, this state would be passed as extra
arguments to the operations that perform the traversal, or they might
appear as global variables.
6. Breaking encapsulation. Visitor's approach assumes that the ConcreteElement interface is powerful enough to let visitors do their job.
As a result, the pattern oftenforces you to provide public operations that
access an element's internal state, which may compromise its encapsulation.
๊ด๋ จ ํจํด๋ค :
Composite :Visitors can be used to apply an operation over an object structure defined by the Composite pattern.
Interpreter :Visitor may be applied to do the interpretation.
์ถ๊ฐ ์ ๋ณด :
- The abstract syntax tree of Interpreter is a Composite (therefore
Iterator and Visitor are also applicable).
- Iterator can traverse a Composite. Visitor can apply an operation over
a Composite.
- The Visitor pattern is like a more powerful Command pattern because the
visitor may initiate whatever is appropriate for the kind of object it
encounters.
- The Visitor pattern is the classic technique for recovering lost type
information without resorting to dynamic casts.
์ถ์ฒ : http://sourcemaking.com/design_patterns/visitor
Design Patterns : Element of Reusab