Visitor Pattern
Visitor Pattern에 대해 설명하는 페이지입니다.
Environment
- Programming Language: Java
Index
Introduction
- Purpose
- Allowing one or more operations to be applied to a set of objects at runtime
- Decoupling the operations from the object structure (= the set of objects)
It’s important that the visitor pattern is used when the above two purposes are needed.
- Use When
- An object structure must have many unrelated operations performed upon it
- The object structure can’t change but operations on it can
- Operations must be performed on the concrete classes of an object structure
- Operations should be able to operate on multiple object structures that implement the same interface sets
Characteristics
- Pros
- Makes adding new operations easy
- You can define a new operation simply by adding a new visitor
- In contrast, if you spread functionality over many classes, then you must change each class to define a new operation
- Gathers related opertations and separates unrelated operations
- Related behavior is not spread over the classes defining the object structure; it’s localized in a visitor
- Unrelated sets of behavior are partitioned in their own visitor classes
- Makes adding new operations easy
- Cons
- 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
- Breaking encapsulation
- Visitor’s approach assumes that the ConcreteElement interface is powerful enough to let visitors do their job
- The pattern often forces you to provide public operations that access an element’s internal state, which may compromise its encapsulation
- Adding new ConcreteElement classes is hard
- Visitors can visit objects that don’t have a common parent class.
Participants
- Visitor
- declares a visit operation for each class of ConcreteElement in the object structure
- ConcreteVisitor
- implements each operation declared by Visitor
- Element
- defines an Accept operation that takes a visitor as an argument
- ConcreteElement
- implements an Accept operation that takes a visitor as an argument
- ObjectStructure
- can enumerate its elements
- may provide a high-level interface to allow the visitor to visit its elements
- may be a composite or a collection like a set or list
How to Use (Example)
- Visitor
public interface ICarElementVisitor { public void visit(Wheel wheel); public void visit(Engine engine); public void visit(Body body); public void visit(Car car); }
- ConcreteVisitor
public class CarElementPrintVisitor implements ICarElementVisitor { public void visit(Wheel wheel) { System.out.println("Visiting " + wheel.getName() + " wheel"); } public void visit(Engine wheel) { System.out.println("Visiting engine"); } public void visit(Body body) { System.out.println("Visiting body"); } public void visit(Car car) { System.out.println("Visiting car"); } }
public class CarElementDoVisitor implements ICarElementVisitor { public void visit(Wheel wheel) { System.out.println("Kicking my " + wheel.getName() + " wheel"); } public void visit(Engine wheel) { System.out.println("Starting my engine"); } public void visit(Body body) { System.out.println("Moving my body"); } public void visit(Car car) { System.out.println("Starting my car"); } }
- Element
public interface ICarElement { public void accept(ICarElementVisitor visitor); }
- ConcreteElement
public class Wheel implements ICarElement { private String name; public Wheel(String name) { this.name = name; } public String getName() { return this.name; } @Override public void accept(ICarElementVisitor visitor) { visitor.visit(this); } }
public class Engine implements ICarElement { @Override public void accept(ICarElementVisitor visitor) { visitor.visit(this); } }
public class Body implements ICarElement { @Override public void accept(ICarElementVisitor visitor) { visitor.visit(this); } }
public class Car implements ICarElement { private ICarElement[] elements; public Car() { this.elements = new ICarElement[] { new Wheel("front left"), new Wheel("front right"), new Wheel("back left") , new Wheel("back right"), new Body(), new Engine() }; } public void accept(ICarElementVisitor visitor) { for(ICarElement elem : elements) elem.accept(visitor); visitor.visit(this); } }