Computer Basis/디자인패턴 및 설계이론

[디자인 패턴] 방문자 패턴(Visitor Pattern)

DevPing9_ 2022. 6. 4. 16:45

# 방문자 패턴 (Vistor Pattern)

SOP 달성, 버그 가능성, 코드 응집성 등의 이유로
기존코드를 건드리지 않고 새로운 기능을 추가하는 방법을 제안하는 패턴

 

 

# 방문자 패턴을 적용하기 전

아래와 같이 동일한 웹 페이지가 기기별로 다르게 렌더링 되어야 한다는 요청사항이 생겼다고 하자.

 

요청사항에 따라,
본래는 view 패키지의 View 의 집합들 뿐이었으나
이제 device 패키지가 추가되었다.

방문자 패턴을 적용하지 않는다면 아래와 같이 코딩을 할 수도 있다.
//View Interface
public interface View {
    // render() -> render(Device device) 로 변경
    void render(Device device);
}

//HomeView Class
public class HomeView implements View{
    // 인터페이스 변경으로 인한 파라미터 Device 로 인한 코드변경
    @Override
    public void render(Device device) 
        // new Logic Created
        if(device instanceof Watch) System.out.println("HomePage Rendered in Watch");
        else if (device instanceof Pad) System.out.println("HomePage Rendered in Pad");
        else if(device instanceof Desktop) System.out.println("HomePage Rendered in Desktop");
        
        /* some shared logics here */
    }
}

//LoginView Class
public class LoginView implements View{
    // 인터페이스 변경으로 인한 파라미터 Device 로 인한 코드변경
    @Override
    public void render(Device device) {
        // new Logic Created
        if(device instanceof Watch) System.out.println("LoginPage Rendered in Watch");
        else if (device instanceof Pad) System.out.println("LoginPage Rendered in Pad");
        else if(device instanceof Desktop) System.out.println("LoginPage Rendered in Desktop");
        
        /* some shared logics here */
    }
}

 

Interface -> class 까지 변경이 전파되며, 다소 반복적인 코드가 상속받은 클래스들에게 구현이 강제된다.
새로운 Device 들이 정의 될 때마다 모든 View 클래스들의 수정이 일어난다.
새로운 View 들이 정의 될 때마다 모든 Device 에 대한 렌더링(render())을 추가적으로 오버라이딩 해야한다.

덕분에 OCP 도 깨졌고,
`화면을 렌더링하는 render() 가 View 클래스들의 역할인가?` 라고 생각이 들면,
SOP 또한 깨졌다고 볼 수 있다.

 

Client-Side

public class Client {
    public static void main(String[] args) {
        HomeView homeView = new HomeView();
        Device device = new Pad();
        homeView.render(device);
    }
}

 

 


 

# 방문자 패턴의 적용

Double Dispatch 를 사용한다.
아래에서 예시코드를 들어 설명하겠다.

Visitor 인터페이스를 정의 후, 상속받은 Vistor 에서 메소드 오버로딩으로 구현하며
Vistor 를 사용할 쪽에서 accept(Visotor) 메소드를 구현한다.

 

 

방문자 패턴이 적용된 코드

 

Visitor Interface 는 Device Interface 이며,
ConcreteVisitor 는 Desktop, Pad, Watch 이다.

 

// Visitor Interface
public interface Device {
    void render(LoginView loginView);
    void render(HomeView homeView);
}

// Concreate Visitor
public class Desktop implements Device {
    @Override
    public void render(LoginView loginView) {
        System.out.println("LoginView rendered in Desktop");
    }

    @Override
    public void render(HomeView homeView) {
        System.out.println("HomeView rendered in Desktop");
    }
}

// Element Interface
public interface View {
    void accept(Device device);
}

// Concreate Element
public class LoginView implements View {
    @Override
    public void accept(Device device) {
        device.render(this);
    }
}

 

새로운 Device 가 생성되어도 View 에선 코드 수정이 일어나지 않는다.
새로운 View 가 생성되면 Device 에서 새로운 View 에 대한 render() 의 구현의 책임을 진다.

SOP 가 깔끔하게 떨어지지 않았는가!!

이전에는 새로운 Device 가 추가되면 모든 View 가 갯수만큼 코드 수정이 일어났다.
물론 새로운 View 가 추가되면 모든 Device 에서 코드 수정이 일어나야 하지만
이 또한 다른 패턴을 더 공부하면 해결할 수 있으리라고 믿어본다... 🤔

일단 Device 에 대한 View 의 의존성은 사라졌다.
그리고 기존 코드(View)를 변경하지 않겠다 라는 목적을 달성시켰다.

 

Client-Side

public class Client {

    public static void main(String[] args) {
        HomeView homeView = new HomeView();
        Device device = new Desktop();
        homeView.accept(device);
    }
}

 

Double dispatch

런타임에 구체적인 타입을 찾아가는 것을 dispatch 라고 한다.

위의 클라이언트 코드
homeView.accept(device) 에서
런타임에 어떤 타입의 Device 를 accept() 하는지 찾는 dispatch 가 1번,
어떤 타입의 View 를 render() 하는지 dispatch 가 1번

총 2번 발생하여 double dispatch 라고 한다.  

 


방문자 패턴의 장점

기존코드의 변경이 거의 없다.
새로운 추가 기능들을 한 곳에 모아 놓을 수 있다.

 

방문자 패턴의 단점

위의 그림과 같이 Double Dispatch 라는 단어가 주는 흐름파악이 복잡하다.
새로운 Element(예제에서는 View)를 추가/삭제 할 때마다 모든 Visitor(Device)에 대한 수정이 필요하다

 

 


 

# Reference

 

코딩으로 학습하는 GoF의 디자인 패턴 - 인프런 | 강의

디자인 패턴을 알고 있다면 스프링 뿐 아니라 여러 다양한 기술 및 프로그래밍 언어도 보다 쉽게 학습할 수 있습니다. 또한, 보다 유연하고 재사용성이 뛰어난 객체 지향 소프트웨어를 개발할

www.inflearn.com

728x90