
C++로 작성된 비지터
비지터는 기존 코드를 변경하지 않고 기존 클래스 계층구조에 새로운 행동들을 추가할 수 있도록 하는 행동 디자인 패턴입니다.
제 설명글 [비지터와 이중 디스패치]{비지터와 이중 디스패치}에서 왜 단순히 비지터들을 메서드 오버로딩으로 대체할 수 없는지 알아보세요.
복잡도:
인기도:
사용 사례들: 비지터는 복잡하고 적용 범위가 좁기 때문에 매우 일반적인 패턴이 아닙니다.
개념적인 예시
이 예시는 비지터 패턴의 구조를 보여주고 다음 질문에 중점을 둡니다:
- 패턴은 어떤 클래스들로 구성되어 있나요?
- 이 클래스들은 어떤 역할을 하나요?
- 패턴의 요소들은 어떻게 서로 연관되어 있나요?
main.cc: 개념적인 예시
/** * The Visitor Interface declares a set of visiting methods that correspond to * component classes. The signature of a visiting method allows the visitor to * identify the exact class of the component that it's dealing with. */ class ConcreteComponentA; class ConcreteComponentB; class Visitor { public: virtual void VisitConcreteComponentA(const ConcreteComponentA *element) const = 0; virtual void VisitConcreteComponentB(const ConcreteComponentB *element) const = 0; }; /** * The Component interface declares an `accept` method that should take the base * visitor interface as an argument. */ class Component { public: virtual ~Component() {} virtual void Accept(Visitor *visitor) const = 0; }; /** * Each Concrete Component must implement the `Accept` method in such a way that * it calls the visitor's method corresponding to the component's class. */ class ConcreteComponentA : public Component { /** * Note that we're calling `visitConcreteComponentA`, which matches the * current class name. This way we let the visitor know the class of the * component it works with. */ public: void Accept(Visitor *visitor) const override { visitor->VisitConcreteComponentA(this); } /** * Concrete Components may have special methods that don't exist in their base * class or interface. The Visitor is still able to use these methods since * it's aware of the component's concrete class. */ std::string ExclusiveMethodOfConcreteComponentA() const { return "A"; } }; class ConcreteComponentB : public Component { /** * Same here: visitConcreteComponentB => ConcreteComponentB */ public: void Accept(Visitor *visitor) const override { visitor->VisitConcreteComponentB(this); } std::string SpecialMethodOfConcreteComponentB() const { return "B"; } }; /** * Concrete Visitors implement several versions of the same algorithm, which can * work with all concrete component classes. * * You can experience the biggest benefit of the Visitor pattern when using it * with a complex object structure, such as a Composite tree. In this case, it * might be helpful to store some intermediate state of the algorithm while * executing visitor's methods over various objects of the structure. */ class ConcreteVisitor1 : public Visitor { public: void VisitConcreteComponentA(const ConcreteComponentA *element) const override { std::cout << element->ExclusiveMethodOfConcreteComponentA() << " + ConcreteVisitor1\n"; } void VisitConcreteComponentB(const ConcreteComponentB *element) const override { std::cout << element->SpecialMethodOfConcreteComponentB() << " + ConcreteVisitor1\n"; } }; class ConcreteVisitor2 : public Visitor { public: void VisitConcreteComponentA(const ConcreteComponentA *element) const override { std::cout << element->ExclusiveMethodOfConcreteComponentA() << " + ConcreteVisitor2\n"; } void VisitConcreteComponentB(const ConcreteComponentB *element) const override { std::cout << element->SpecialMethodOfConcreteComponentB() << " + ConcreteVisitor2\n"; } }; /** * The client code can run visitor operations over any set of elements without * figuring out their concrete classes. The accept operation directs a call to * the appropriate operation in the visitor object. */ void ClientCode(std::array<const Component *, 2> components, Visitor *visitor) { // ... for (const Component *comp : components) { comp->Accept(visitor); } // ... } int main() { std::array<const Component *, 2> components = {new ConcreteComponentA, new ConcreteComponentB}; std::cout << "The client code works with all visitors via the base Visitor interface:\n"; ConcreteVisitor1 *visitor1 = new ConcreteVisitor1; ClientCode(components, visitor1); std::cout << "\n"; std::cout << "It allows the same client code to work with different types of visitors:\n"; ConcreteVisitor2 *visitor2 = new ConcreteVisitor2; ClientCode(components, visitor2); for (const Component *comp : components) { delete comp; } delete visitor1; delete visitor2; return 0; }
Output.txt: 실행 결과
The client code works with all visitors via the base Visitor interface: A + ConcreteVisitor1 B + ConcreteVisitor1 It allows the same client code to work with different types of visitors: A + ConcreteVisitor2 B + ConcreteVisitor2