
C#으로 작성된 비지터
비지터는 기존 코드를 변경하지 않고 기존 클래스 계층구조에 새로운 행동들을 추가할 수 있도록 하는 행동 디자인 패턴입니다.
제 설명글 [비지터와 이중 디스패치]{비지터와 이중 디스패치}에서 왜 단순히 비지터들을 메서드 오버로딩으로 대체할 수 없는지 알아보세요.
복잡도:
인기도:
사용 사례들: 비지터는 복잡하고 적용 범위가 좁기 때문에 매우 일반적인 패턴이 아닙니다.
개념적인 예시
이 예시는 비지터 패턴의 구조를 보여주고 다음 질문에 중점을 둡니다:
- 패턴은 어떤 클래스들로 구성되어 있나요?
- 이 클래스들은 어떤 역할을 하나요?
- 패턴의 요소들은 어떻게 서로 연관되어 있나요?
Program.cs: 개념적인 예시
using System; using System.Collections.Generic; namespace RefactoringGuru.DesignPatterns.Visitor.Conceptual { // The Component interface declares an `accept` method that should take the // base visitor interface as an argument. public interface IComponent { void Accept(IVisitor visitor); } // 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. public class ConcreteComponentA : IComponent { // 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(IVisitor visitor) { 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. public string ExclusiveMethodOfConcreteComponentA() { return "A"; } } public class ConcreteComponentB : IComponent { // Same here: VisitConcreteComponentB => ConcreteComponentB public void Accept(IVisitor visitor) { visitor.VisitConcreteComponentB(this); } public string SpecialMethodOfConcreteComponentB() { return "B"; } } // 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. public interface IVisitor { void VisitConcreteComponentA(ConcreteComponentA element); void VisitConcreteComponentB(ConcreteComponentB element); } // 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 : IVisitor { public void VisitConcreteComponentA(ConcreteComponentA element) { Console.WriteLine(element.ExclusiveMethodOfConcreteComponentA() + " + ConcreteVisitor1"); } public void VisitConcreteComponentB(ConcreteComponentB element) { Console.WriteLine(element.SpecialMethodOfConcreteComponentB() + " + ConcreteVisitor1"); } } class ConcreteVisitor2 : IVisitor { public void VisitConcreteComponentA(ConcreteComponentA element) { Console.WriteLine(element.ExclusiveMethodOfConcreteComponentA() + " + ConcreteVisitor2"); } public void VisitConcreteComponentB(ConcreteComponentB element) { Console.WriteLine(element.SpecialMethodOfConcreteComponentB() + " + ConcreteVisitor2"); } } public class Client { // 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. public static void ClientCode(List<IComponent> components, IVisitor visitor) { foreach (var component in components) { component.Accept(visitor); } } } class Program { static void Main(string[] args) { List<IComponent> components = new List<IComponent> { new ConcreteComponentA(), new ConcreteComponentB() }; Console.WriteLine("The client code works with all visitors via the base Visitor interface:"); var visitor1 = new ConcreteVisitor1(); Client.ClientCode(components,visitor1); Console.WriteLine(); Console.WriteLine("It allows the same client code to work with different types of visitors:"); var visitor2 = new ConcreteVisitor2(); Client.ClientCode(components, visitor2); } } }
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