본문 바로가기

설계

[디자인 패턴] 비지터 패턴 (Visitor Pattern) - python 예제 코드

Visitor Pattern은 객체 지향 디자인 패턴 중 하나로, 객체를 탐색하고 객체의 각 요소에 대해 작업을 수행하는 방법을 제공합니다. 이 패턴은 객체의 구조와 작업을 분리하고, 새로운 작업을 추가하거나 객체 구조를 수정하지 않고도 작업을 수행할 수 있게 합니다.

패턴의 핵심 아이디어는 방문자(visitor)라는 개체를 사용하는 것입니다. 방문자는 객체 구조를 탐색하고 각 객체에 대해 특정 작업을 수행합니다. 이 때 객체는 방문자를 인자로 전달하고, 방문자는 객체의 메소드를 호출하여 작업을 수행합니다.

1. 예제 코드


Python 예제 코드를 통해 이해해보겠습니다.

class Element:
    def accept(self, visitor):
        pass

class ConcreteElementA(Element):
    def accept(self, visitor):
        visitor.visit_concrete_element_a(self)

    def operation_a(self):
        pass

class ConcreteElementB(Element):
    def accept(self, visitor):
        visitor.visit_concrete_element_b(self)

    def operation_b(self):
        pass

class Visitor:
    def visit_concrete_element_a(self, element):
        pass

    def visit_concrete_element_b(self, element):
        pass

class ConcreteVisitor1(Visitor):
    def visit_concrete_element_a(self, element):
        element.operation_a()

    def visit_concrete_element_b(self, element):
        element.operation_b()

class ConcreteVisitor2(Visitor):
    def visit_concrete_element_a(self, element):
        pass

    def visit_concrete_element_b(self, element):
        element.operation_b()


위 예제 코드에서, Element 클래스는 객체 구조의 기본 클래스입니다. ConcreteElementA와 ConcreteElementB는 실제 객체입니다. accept 메소드는 각 객체에서 호출됩니다.

Visitor 클래스는 방문자 클래스입니다. visit_concrete_element_a와 visit_concrete_element_b 메소드는 각 요소를 처리하기 위해 사용됩니다. ConcreteVisitor1과 ConcreteVisitor2는 방문자 클래스를 상속받은 구체적인 방문자 클래스입니다.

ConcreteVisitor1은 ConcreteElementA와 ConcreteElementB 객체에서 작업을 수행합니다. visit_concrete_element_a 메소드에서는 operation_a를 호출하고, visit_concrete_element_b 메소드에서는 operation_b를 호출합니다.

ConcreteVisitor2는 ConcreteElementA 객체에서는 아무 작업도 수행하지 않고, ConcreteElementB 객체에서는 operation_b를 호출합니다.

이제 이러한 클래스를 사용하여 객체 구조를 탐색하고 작업을 수행하는 방법을 살펴보겠습니다.

def main():
    elements = [ConcreteElementA(), ConcreteElementB()]
    visitor1 = ConcreteVisitor1()
    visitor2 = ConcreteVisitor2()

    for element in elements:
        element.accept(visitor1)
        element.accept(visitor2)

if __name__ == '__main__':
    main()


main 함수에서는 ConcreteElementA와 ConcreteElementB 객체를 생성하고, ConcreteVisitor1과 ConcreteVisitor2 객체를 생성합니다.

그리고 elements 리스트에 있는 각 요소에 대해 accept 메소드를 호출하여 visitor1과 visitor2가 작업을 수행합니다.

visitor1은 ConcreteElementA와 ConcreteElementB 객체에서 모두 작업을 수행하므로, operation_a와 operation_b 메소드가 각 객체에서 호출됩니다.

visitor2는 ConcreteElementA 객체에서 아무 작업도 수행하지 않고, ConcreteElementB 객체에서는 operation_b 메소드가 호출됩니다.

이렇게 패턴을 사용하면 객체의 구조와 작업을 분리하고, 새로운 작업을 추가하거나 객체 구조를 수정하지 않고도 작업을 수행할 수 있습니다. 하지만 이 패턴은 코드가 복잡해질 수 있으므로, 상황에 따라 적합한 패턴을 선택해야 합니다.

2. 장점

  • 객체 구조와 작업을 분리하므로, 새로운 작업을 추가하거나 객체 구조를 수정하지 않고도 작업을 수행할 수 있습니다.
  • 객체 간의 의존성을 줄일 수 있습니다.
  • 구조와 작업의 분리로 코드의 재사용성과 유지보수성을 높일 수 있습니다.

3. 단점

  • 구현이 복잡해질 수 있습니다.
  • 객체 구조가 바뀌면 Visitor 인터페이스와 모든 구체 Visitor 클래스를 수정해야 합니다.
  • 객체 구조가 복잡해지면 방문자의 수가 증가하여 코드가 복잡해질 수 있습니다.

따라서, 객체 구조와 작업을 분리해야 하는 상황에서 Visitor 패턴은 유용한 디자인 패턴 중 하나입니다. 하지만 구조와 작업의 복잡성에 따라 패턴을 선택하고 구현하는 것이 중요합니다.