# 방문자 (Visitor) 패턴

# 방문자 패턴이란 무엇인가요?

정의

기존 코드를 변경하지 않고 새로운 기능을 추가하는 방법

  • SRP를 준수하고자, 더는 책임을 늘리고 싶지 않을 때
  • 버그가 발생 우려로 인하여, 수정을 더 이상 진행하고 싶지 않을 때
  • 반복해서 비슷한 코드가 작성되는 상황이 발생할 수 있을 거 같을 때

중요!

더블 디스패치 (Double Dispatch)를 활용할 수 있다

image

  • 어느 엔리먼트의 accept 메소드 인지 파악하기 위한 디스패치 한 번
  • 비지터의 어느 visit 메소드인지 파악하기 위한 디스패치 한 번

# 비지터 패턴은 언제 적용되어야 할까요?

상황가정

다양한 도형을 다양한 디바이스에서 그리는 상황.
이때 도형들은 디바이스마다 크기가 다르게 출력되어야 한다.

public interface Galaxy {}

public class S22 implements Galaxy {}

public class Watch implements Galaxy {}
  • 도형들이 그려질 디바이스이며, 상위 인터페이스인 Galaxy, 하위 구현제 S22Watch 이다.
public interface Shape {
    void printTo(Galaxy device);
}

public class Triangle implements Shape {
    @Override
    public void printTo(Galaxy device) {
        if (device instanceof S22) {
            System.out.println("print Triangle to S22");
        } else if (device instanceof Watch) {
            System.out.println("print Triangle to Watch");
        }
    }
}

public class Circle implements Shape {
    @Override
    public void printTo(Galaxy device) {
        if (device instanceof S22) {
            System.out.println("print Circle to S22");
        } else if (device instanceof Watch) {
            System.out.println("print Circle to watch");
        }
    }
}

public class Rectangle implements Shape {
    @Override
    public void printTo(Galaxy device) {
        if (device instanceof S22) {
            System.out.println("print Rectangle to S22");
        } else if (device instanceof Watch) {
            System.out.println("print Rectangle to watch");
        }
    }
}
  • 도형은 상위 인터페이스인 Shape 가 존재하고, 하위 구현체 Rectangle, Circle, Triangle는 각 디바이스에 따른 메소드를 구현한다.
  • 기기마다 다른 기능 수행으로 인해 위와 같은 분기가 발생한다.

DANGER

출력하는 게 왜 도형의 역할인가?
그리고 디바이스가 늘어난다면 코드는 어떻게 될까?
새로운 도형이 생기면 또 디바이스마다 처리해줘야 하겠지?

# 비지터 패턴 적용하기

public interface Galaxy {
    void print(Circle circle);
    void print(Rectangle rectangle);
    void print(Triangle triangle);
}
  • Shape의 그림을 그리는 로직을 모두 device로 옮기고, Shape에는 accept 메서드 하나만 만들어준다.
  • 각 도형들을 그리는 기능은 오버로딩을 이용하여, 각 도형에 대한 모든 메소드들을 가지도록 한다.
public class S22 implements Galaxy {
    @Override
    public void print(Circle circle) {
        System.out.println("Print Circle to S22");
    }

    @Override
    public void print(Rectangle rectangle) {
        System.out.println("Print Rectangle to S22");
    }

    @Override
    public void print(Triangle triangle) {
        System.out.println("Print Triangle to S22");
    }
}
public class Watch implements Galaxy {
    @Override
    public void print(Circle circle) {
        System.out.println("Print Circle to Watch");
    }

    @Override
    public void print(Rectangle rectangle) {
        System.out.println("Print Rectangle to Watch");
    }

    @Override
    public void print(Triangle triangle) {
        System.out.println("Print Triangle to Watch");
    }
}
public interface Shape {
    void accept(Galaxy device);
}
  • 각 Shape 구현체들은 Galaxy로 옮겨진 print메서드를 호출한다.
public class Circle implements Shape {
    @Override
    public void accept(Galaxy device) {
        device.print(this);
    }
}
public class Rectangle implements Shape {
    @Override
    public void accept(Galaxy device) {
        device.print(this);
    }
}
public class Triangle implements Shape {
    @Override
    public void accept(Galaxy device) {
        device.print(this);
    }
}

더블디스패치

어느 도형의 accept 인지 디스패치하고, 어떤 디바이스의 print 인지 디스패치함으로써 두번 디스패치가 발생한다.

# 방문자 패턴의 장점과 단점

장점

  • 기존 코드를 변경하지 않고 새로운 코드를 추가할 수 있다.
  • 추가 기능을 한 곳에 모아둘 수 있다.

단점

  • 구조가 복잡하고, 새로운 Element를 추가하거나 제거할 때 모든 Visitor 코드를 변경해야 한다.

# 방문자 패턴의 예시

자바

  • FileVisitor, SimpleFileVisitor
  • AnnotationValueVisitor
  • ElementVisitor

스프링

  • BeanDefinitionVisitor
    빈정의를 읽어들일때 사용하는 거라 우리가 쓸 일은 없다.

# 출처

  • https://kingchan223.tistory.com/332