24. [Design Pattern] 방문자 패턴(Visitor)

2024. 2. 29. 01:33· 프로그래밍 이론/디자인 패턴
목차
  1. 방문자 패턴
  2. 구조
  3. 기본 인터페이스
  4. 왜 사용해야 할까?
  5.  
  6. 장점
  7. 단점
  8. 전체 예시 코드
  9. 참고자료

방문자 패턴

객체의 구조와 객체에 수행되는 작업을 분리한다. 기존 클래스를 변경하지 않고도 새로운 작업을 정의할 수 있게 한다.


구조

방문자 클래스 다이어그램

  • Visitor - 방문자 클래스들이 구현해야 할 인터페이스. 각 구체적인 요소 클래스(Element)를 방문(visit)하는 메소드를 선언한다.
  • Concrete Visitor - Visitor 인터페이스를 구현하는 클래스. 구체적인 요소(Element)들에 대한 방문 동작을 구현한다. 각 요소에 대해 수행할 작업을 정의한다.
  • Element - 요소 클래스들이 구현해야 할 인터페이스. 이 인터페이스는 Accept 메소드를 선언하여, 방문자 객체가 요소 객체를 방문할 수 있게 한다.
  • Concrete Element - Element 인터페이스를 구현하는 클래스. 실제 데이터와 작업을 포함한다. accept 메소드에서는 자신을 인자로 하여 방문자의 visit 메소드를 호출한다.
  • Object Structure - 요소 객체들의 집합. 방문자가 방문할 수 있는 구조를 제공한다.

기본 인터페이스

// Visitor
public interface IVisitor
{
    void VisitConcreteElementA(ConcreteElementA elementA);
    void VisitConcreteElementB(ConcreteElementB elementB);
}

// ConcreteVisitor1
public class ConcreteVisitor1 : IVisitor
{
    public void VisitConcreteElementA(ConcreteElementA elementA)
    {
        Console.WriteLine($"{elementA.GetType().Name} visited by {this.GetType().Name}");
    }

    public void VisitConcreteElementB(ConcreteElementB elementB)
    {
        Console.WriteLine($"{elementB.GetType().Name} visited by {this.GetType().Name}");
    }
}

// ConcreteVisitor2
public class ConcreteVisitor2 : IVisitor
{
    public void VisitConcreteElementA(ConcreteElementA elementA)
    {
        // 다른 구현이 가능합니다.
    }

    public void VisitConcreteElementB(ConcreteElementB elementB)
    {
        // 다른 구현이 가능합니다.
    }
}
// Element
public interface IElement
{
    void Accept(IVisitor visitor);
}

// ConcreteElementA
public class ConcreteElementA : IElement
{
    public void Accept(IVisitor visitor)
    {
        visitor.VisitConcreteElementA(this);
    }
}

// ConcreteElementB
public class ConcreteElementB : IElement
{
    public void Accept(IVisitor visitor)
    {
        visitor.VisitConcreteElementB(this);
    }
}
class Program
{
    static void Main(string[] args)
    {
        // 각 요소 인스턴스 생성
        ConcreteElementA elementA = new ConcreteElementA();
        ConcreteElementB elementB = new ConcreteElementB();

        // 방문자 인스턴스 생성
        ConcreteVisitor1 visitor1 = new ConcreteVisitor1();
        ConcreteVisitor2 visitor2 = new ConcreteVisitor2();

        // 방문자를 각 요소에 적용
        elementA.Accept(visitor1);
        elementB.Accept(visitor1);
        // 결과:
        // "ConcreteElementA visited by ConcreteVisitor1"
        // "ConcreteElementB visited by ConcreteVisitor1"

        elementA.Accept(visitor2);
        elementB.Accept(visitor2);
        // 결과: ConcreteVisitor2의 구현에 따라 달라질 수 있습니다.
    }
}

왜 사용해야 할까?

 방문자 패턴은 프로그래밍에서 복잡한 객체 구조에 다양한 연산을 수행하고자 할 때 유용한 디자인 패턴이다. 이 패턴의 핵심 아이디어는 객체 구조와 객체에 대한 작업을 분리하는 것이다. 즉, 객체의 클래스를 변경하지 않고도 새로운 연산을 객체에 추가할 수 있다.

같은 작품을 여러 방식으로 바라보는 사람들

 방문자 패턴을 이해하기 위해 일상적인 상황을 상상해보자. 박물관을 방문하는 상황을 생각해보면 좋을 것 같다. 박물관에는 다양한 전시물이 있고, 이 전시물들은 박물관의 구조를 이룬다. 이제 박물관에는 다양한 배경지식을 가진 방문자들이 찾아온다. 이 방문자들은 예술가, 역사학자, 어린이 등 다양하다.

 각각의 방문자는 전시물을 자신만의 방식으로 해석하고 경험한다. 예술가는 예술적 관점에서, 역사학자는 역사적 관점에서 전시물을 살펴볼 것이다. 어린이는 또 다른 시각에서 전시물을 경험하겠지. 이렇게 각각의 방문자는 전시물에 다른 "작업"을 수행한다.

 박물관의 전시물 자체는 변하지 않는다. 변하는 것은 방문자와 그들이 전시물을 경험하는 방식이다.

 

 이제 이 예를 프로그래밍의 세계로 옮겨보자. 프로그래밍에서, "박물관"은 객체의 집합 또는 복잡한 데이터 구조를 나타낸다. 이 구조 안에는 여러 "전시물"이 있는데, 이것들은 프로그램에서 다루는 다양한 객체들이다.

 "방문자"는 이 데이터 구조에 대해 특정 작업을 수행하는 역할을 한다. 방문자 객체는 다양한 타입의 요소들을 '방문'하며, 각 요소에 특정 작업을 수행한다. 예를 들어, 데이터를 저장, 분석, 보고하는 등의 작업을 수행할 수 있다.

 

 방문자 패턴의 장점은 새로운 작업을 추가하려면 새로운 방문자를 만들기만 하면 된다는 것이다. 이는 기존의 객체 구조를 변경하지 않고도 새로운 기능을 추가할 수 있게 해준다. 또한, 데이터 구조와 작업 로직을 분리함으로써, 각각을 독립적으로 변경하고 재사용할 수 있다. 이는 프로그램을 더 유연하고 관리하기 쉽게 만들어 준다.

프로그래밍에서의 한 예시를 들어보겠다. 

게임 월드 내 다양한 객체에 대한 상호작용

 게임 월드에는 다양한 객체들이 존재한다(예: 캐릭터, 건물, 자연 환경 등). 게임 플레이어 또는 AI가 이 객체들과 상호작용할 때, 각 객체에 대해 수행해야 하는 동작이 다를 수 있다. 예를 들어, 플레이어가 건물에 접근했을 때는 건물을 조사하거나 사용하는 로직이, 나무에 접근했을 때는 나무를 채취하는 로직이 필요할 수 있다. 여기서 각 객체는 'Element'가 되며, 상호작용 로직은 'Visitor'가 된다. 이 방식으로 게임 월드 내 다양한 객체들과의 상호작용을 유연하게 관리할 수 있다.

1. Visitor

public interface IGameElementVisitor
{
    void VisitBuilding(Building building);
    void VisitTree(Tree tree);
    void VisitCharacter(Character character);
}

 Visitor는 게임 내 다양한 객체들과의 상호작용을 정의하는 방문자들이 구현해야 하는 메소드들을 제공한다. 이 코드에서  IGameElementVisitor는 VisitBuilding, VisitTree, VisitCharacter 등의 메소드를 통해 구체적인 객체 유형에 대한 상호작용 로직을 정의한다.

2. Concrete Visitor

public class InteractionVisitor : IGameElementVisitor
{
    public void VisitBuilding(Building building)
    {
        // 건물과의 상호작용 로직
        Console.WriteLine("Interacting with a building.");
    }

    public void VisitTree(Tree tree)
    {
        // 나무와의 상호작용 로직
        Console.WriteLine("Interacting with a tree.");
    }

    public void VisitCharacter(Character character)
    {
        // 다른 캐릭터와의 상호작용 로직
        Console.WriteLine("Interacting with another character.");
    }
}

 Concrete Visitor는 Visitor 인터페이스를 구현하는 실제 클래스이다. InteractionVisitor 클래스는 게임 내 다양한 요소(예: 건물, 나무, 캐릭터)와의 구체적인 상호작용을 정의한다. 

3. Element Interface

public interface IGameElement
{
    void Accept(IGameElementVisitor visitor);
}

 Element는 Visitor가 Element 객체를 방문할 수 있게 한다. 위의 Element 인터페이스는 게임 내의 모든 상호작용 가능한 요소가 구현해야 하는 인터페이스다. Accept 메소드를 통해 방문자 객체를 받아들여 해당 방문자에 정의된 상호작용을 수행한다. 이를 통해, 각 요소는 방문자에 따라 다른 방식으로 상호작용할 수 있다.

4. Concrete Element

public class Building : IGameElement
{
    public void Accept(IGameElementVisitor visitor)
    {
        visitor.VisitBuilding(this);
    }
}

public class Tree : IGameElement
{
    public void Accept(IGameElementVisitor visitor)
    {
        visitor.VisitTree(this);
    }
}

public class Character : IGameElement
{
    public void Accept(IGameElementVisitor visitor)
    {
        visitor.VisitCharacter(this);
    }
}

 IGameElement 인터페이스를 구현하는 구체적인 클래스들이다. Building, Tree, Character 클래스는 게임 내의 건물, 나무, 캐릭터 등을 나타낸다. 각 클래스는 Accept 메소드를 오버라이드하여, 자신의 유형에 맞는 방문자 메소드를 호출한다. 

 ex) Building 클래스는 VisitBuilding 메소드를 호출한다.

결과

class Program
{
    static void Main(string[] args)
    {
        IGameElement building = new Building();
        IGameElement tree = new Tree();
        IGameElement character = new Character();

        IGameElementVisitor visitor = new InteractionVisitor();

       // 각 게임 요소에 방문자를 전달하여 상호작용을 수행한다.
        // Accept 메소드는 방문자에 따라 적절한 상호작용을 실행한다.
        building.Accept(visitor);
        tree.Accept(visitor);
        character.Accept(visitor);

        // 이 과정에서 building, tree, character 객체는
        // InteractionVisitor에 정의된 방식대로 각각 다르게 반응한다.
    }
}

 

장점

  1. 기존 코드를 변경하지 않고 새로운 코드를 추가할 수 있다.(OCP 보장)
  2. 추가 기능을 한 곳에 모아둘 수 있다.(SRP 보장)

단점

  1. 새로운 Element를 추가하거나 제거할 때 모든 Visitor 코드를 변경해야 한다.

전체 예시 코드

참고자료

 

GoF의 디자인 패턴 - 예스24

이 책은 디자인 패턴을 다룬 이론서로 디자인 패턴의 기초적이고 전반적인 내용을 학습할 수 있다.

www.yes24.com

 

저작자표시 (새창열림)

'프로그래밍 이론 > 디자인 패턴' 카테고리의 다른 글

23. [Design Pattern] 템플릿 메소드 패턴(Templete Method)  (0) 2024.02.28
22. [Design Pattern] 전략 패턴(Strate)  (0) 2024.02.27
21. [Design Pattern] 옵저버 패턴(Observer)  (1) 2024.02.26
20. [Design Pattern] 상태 패턴(State)  (0) 2024.02.20
19. [Design Pattern] 메멘토(Memento) 패턴  (0) 2024.02.19
  1. 방문자 패턴
  2. 구조
  3. 기본 인터페이스
  4. 왜 사용해야 할까?
  5.  
  6. 장점
  7. 단점
  8. 전체 예시 코드
  9. 참고자료
'프로그래밍 이론/디자인 패턴' 카테고리의 다른 글
  • 23. [Design Pattern] 템플릿 메소드 패턴(Templete Method)
  • 22. [Design Pattern] 전략 패턴(Strate)
  • 21. [Design Pattern] 옵저버 패턴(Observer)
  • 20. [Design Pattern] 상태 패턴(State)
NewtronVania
NewtronVania
개발 내용과 지식을 정리한 블로그입니다.
NewtronVania
Newtron의 프로그래밍 블로그
NewtronVania
전체
오늘
어제
  • 분류 전체보기 (74)
    • 언리얼 (7)
      • 포트폴리오 (1)
    • 유니티 (2)
    • 코딩테스트 (27)
    • 프로그래밍 이론 (38)
      • 디자인 패턴 (25)
      • 알고리즘 (5)
      • 컴퓨터 그래픽스 (2)
      • AI (3)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • A* 알고리즘
  • 백준
  • 게임 역사
  • 유한 상태 머신
  • 코딩테스트
  • 루트 모션
  • q-learing
  • 강화학습
  • Strategy
  • 언리얼 엔진
  • 게임 구현
  • daijkstra
  • DP
  • 디자인 패턴
  • 언리얼 오브젝트
  • CS
  • 모션 워핑
  • Templet Method
  • 비트마스킹
  • 다이나믹 프로그래밍
  • UObject
  • AI
  • 전략 패턴
  • Algorithm
  • Unreal
  • behaviour tree
  • 단어 검색
  • 알고리즘
  • q-learning
  • 행동 트리

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.1
NewtronVania
24. [Design Pattern] 방문자 패턴(Visitor)
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.