-
[디자인 패턴] 방문자 패턴(Visitor Pattern)Computer Basis/디자인패턴 및 설계이론 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
728x90'Computer Basis > 디자인패턴 및 설계이론' 카테고리의 다른 글
[디자인패턴] 템플릿 메소드(template-method) 패턴 & 템플릿 콜백(template-callback) 패턴 (0) 2022.05.22 [디자인패턴] 브릿지 패턴 (Bridge Pattern) (0) 2022.05.08 [설계이론] Project Structure - 프로젝트 구성을 어떻게 설계해야 할까? (패키지 분리기준) (0) 2022.03.27 [디자인패턴] 싱글톤패턴을 쓰는 이유와 주의할 점 (0) 2022.03.15 [디자인패턴] 상속보다는 컴포지션을 사용하자!!! (0) 2022.02.17