본문 바로가기

Design/Design Pattern

[Design Pattern] Visitor Pattern

이번 장에서는 비지터(Visitor) 패턴에 대해서 알아본다.
샘플 코드는 여기 (링크) 프로젝트의 테스트 코드로 정리해두었다.


비지터 패턴이란?
위키백과에 따른 정의는 아래와 같다.

알고리즘을 객체 구조에서 분리시키는 디자인 패턴.
구조를 수정하지 않고도 실질적으로 새로운 동작을 기존의 객체 구조에 추가할 수 있게되어 OCP(개방-폐쇄 원칙)를 지킬 수 있게 하는 패턴.

방문자가 방문 할 방문공간과 방문자를 나누어서 로직을 분리한다.
방문자가 방문한 이후의 행동은 방문자에게 위임하는 패턴.

필자는 카페와 바라는 방문공간에 성인과 미성년자가 방문자로 방문하는 상황을 예로들어 진행하겠다.


GoF Design Pattern에 따르면 아래와 같은 Class Diagram이 그려진다.

Visitor: 방문자 추상 클래스. 방문자가 방문 가능한 Element의 수만큼 추상 메서드를 가지고 있다.

ConcreteVisitor1, ConcreteVisitor2: Visitor를 확장(상속)한 구현체. Element에 방문할 때의 실질적인 행위를 구현하고 있다.

Element: 방문 가능한 곳의 추상 클래스. Visitor의 방문을 accept한다.

ConcreteElementA, ConcreteElementB: 방문자들이 방문하였을 때 방문자에게 자신을 위임(Delegate)하는 역할을 한다.

위의 그림을 카페와 바, 성인과 미성년자로 바꾸면 아래와 같은 Class Diagram이 그려진다.

Store: accept메소드를 정의하고 있으며 방문자를 방문을 허가하는 인터페이스.

Cafe, Bar: Human들이 방문할 Store의 구현체. Human이 자신에게 방문하면 자기 자신을 넘겨서 책임을 위임(Delegate)한다.

Human: Visitor들의 추상 클래스.

Adult, Teenager: Visitor의 확장(하위)클래스. Human이 Store에 방문하였을 때 어떠한 일이 일어나는지 구현하고 있다.


그림만으로는 이해하기 힘들다.
코드를 보면서 비지터 패턴을 이해하고 추후에 새로운 Visitor인 대학생을 추가하면서 어떠한 상황에서 비지터 패턴을 사용해야하는지 이해해본다.

Store Interface
방문자(Human)을 accept하고, 총 방문자 수를 늘리는 addVisitor, 총 좌석수를 구하는 getTotalSeat, 사용중인 좌석수를 구하는 getUsedSeat메서드를 정의하고 있다.

Cafe, Bar: 방문자들이 실제로 방문하는 Store의 구현체
코드를 보면서 사용 가능한 좌석이 없으면 더 이상 방문을 못해야하는거 아닌가?
미성년자가 Bar에 방문했을 때의 예외처리는 왜 없지?
라는 의문이 들었으면 비지터 패턴을 거의 다 이해한 것이다.
accept 메서드를 보면 방문자인 human의 visit 메서드를 호출하면 자기 자신을 넘기고 있다.
Visitor에게 나를 줄테니 너가 알아서 처리해! 라는 의미다.

Human: 방문자 추상 클래스
Bar에 방문하였을 때와 Cafe에 방문하였을 때를 처리하는 메서드를 정의하고 있으며
Store의 좌석수를 확인하여 방문가능한지 확인하는 메서드를 구현하고 있다.

Adult, Teenager: Human을 확장(상속)한 클래스로 자신이 Bar와 Cafe에 방문하였을 때 어떠한 일이 벌어질지 스스로 구현한다.
Cafe와 Bar 클래스에서 구현하지 않았던 예외처리가 여기에 포함되어 있다.

Tester: Store와 Human을 생성시키고 방문시킬 클래스

코드를 실행시킨 결과는 아래와 같다.


비지터 패턴이 뭔지는 알겠는데...왜 쓰는거에요???
이런 의문이 든다면 새로운 Visitor인 대학생을 추가시켜보자.
또한 Cafe와 Bar는 대학생이 방문하는 경우 할인을 해줘야한다고 가정한다.
만약 비지터 패턴이 적용되지 않았다면
Bar의 코드는

  • Adult가 방문했을 때는 남은 좌석만 확인하고
  • Teenager가 방문했을 때는 못들어오게하고
  • 대학생이 방문했을 때는 남은 좌석 확인하고 할인

새로운 비지터가 추가될 때마다 Bar의 코드는 상당히 지저분해질 것이다. 더불어 Cafe의 코드 또한 방대해질 것이다.
하지만 우리는 비지터 패턴으로 구현하였기 때문에 대학생이 추가된다 하더라도 Cafe, Bar의 코드는 변경될 필요가 없다.

Student 클래스를 만들고 새로만든 클래스에 Cafe, Bar에 방문했을 때의 행위만 구현해주면 끝이다.

지금까지 Cafe와 Bar, Adult와 Teenager를 주제로 비지터 패턴에 대해서 알아보았다.