Visitor

2 minute read

Visitor Pattern - 행위 패턴


사용 목적

객체 지향 프로그래밍과 소프트웨어 공학에서 비지터 패턴(visitor pattern; 방문자 패턴)은 알고리즘을 객체 구조에서 분리시키는 디자인 패턴이다. 이렇게 분리를 하면 구조를 수정하지 않고도 실질적으로 새로운 동작을 기존의 객체 구조에 추가할 수 있게 된다. 개방-폐쇄 원칙을 적용하는 방법의 하나이다.


UML Diagram

alt_text

  • visitor(NodeVisitor) : 객체 구조 내에 있는 각 ConcreteElement 클래스를 위한 Visit() 연산을 선언. 또한, 연산의 이름과 인터페이스 형태는 Visit() 요청을 방문자에게 보내는 클래스를 식별한다.

  • ConcreteVisitor(TypeCheckingVisitor) : Visitor 클래스에 선언된 연산을 구현한다. 각 연산은 구조 내에 있는 객체의 대응 클래스에 정의된 일부 알고리즘을 구현한다.

  • Element(Node) : 방문자를 인자로 받아들이는 Accept() 연산을 정의한다.

  • ConcreteElement(AssignmentNode, VariableRefNode) : 인자로 방문자 객체를 받아들이는 Accept() 연산을 구현한다.

  • ObjectStructure(Program) : 객체 구조 내의 원소들을 나열한다. 방문자가 이 원소에 접근하게 하는 상위 수준 인터페이스 또한 제공한다. 이는 Composite 패턴으로 만든 복합체일 수도 있고, 리스트나 집합 등 컬렉션일 수도 있다.


예제

alt_text

// Visitor
interface CarElementVisitor {
    void visit(Wheel wheel);
    void visit(Engine engine);
    void visit(Body body);
    void visit(Car car);
}
// Element
interface CarElement {
    void accept(CarElementVisitor visitor); // CarElements have to provide accept().
}
// ConcreteElement
class Wheel implements CarElement {
    private String name;

    public Wheel(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    public void accept(CarElementVisitor visitor) {
        visitor.visit(this);
    }
}

class Engine implements CarElement {
    public void accept(CarElementVisitor visitor) {
        visitor.visit(this);
    }
}

class Body implements CarElement {
    public void accept(CarElementVisitor visitor) {
        visitor.visit(this);
    }
}
// Composite 패턴이 사용된 복합체 Element
class Car implements CarElement{
    CarElement[] elements;

    public CarElement[] getElements() {
        return elements.clone(); // Return a copy of the array of references.
    }

    public Car() {
        this.elements = new CarElement[]
          { new Wheel("front left"), new Wheel("front right"),
            new Wheel("back left") , new Wheel("back right"),
            new Body(), new Engine() };
    }

    public void accept(CarElementVisitor visitor) {
        for(CarElement element : this.getElements()) {
            element.accept(visitor);
        }
        visitor.visit(this);
    }
}
// ConcreteVisitor
class CarElementPrintVisitor implements CarElementVisitor {
    public void visit(Wheel wheel) {
        System.out.println("Visiting "+ wheel.getName()
                            + " wheel");
    }

    public void visit(Engine engine) {
        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");
    }
}
// 다른 버전의 ConcreteVisitor
class CarElementDoVisitor implements CarElementVisitor {
    public void visit(Wheel wheel) {
        System.out.println("Kicking my "+ wheel.getName() + " wheel");
    }

    public void visit(Engine engine) {
        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");
    }
}
// Client
public class VisitorDemo {
    static public void main(String[] args){
        Car car = new Car();
        car.accept(new CarElementPrintVisitor());
        car.accept(new CarElementDoVisitor());
    }
}
/*
Visiting front left wheel
Visiting front right wheel
Visiting back left wheel
Visiting back right wheel
visiting body
Visiting engine
Kicking my front left wheel
Kicking my front right wheel
Kicking my back left wheel
Kicking my back right wheel
Moving my body
Starting my engine
*/

참고

  • https://lktprogrammer.tistory.com/58
  • http://blog.naver.com/PostView.nhn?blogId=2feelus&logNo=220664244510&parentCategoryNo=&categoryNo=28&viewDate=&isShowPopularPosts=false&from=postView
  • https://leetaehoon.tistory.com/63
  • https://techbard.tistory.com/2869

Updated: