Skip to main content

[Design Pattern] 행동 패턴 7. 비지터 (Visitor)




비지터 (Visitor) #

비지터

  • 객체의 구조(Component)와 처리(Visitor)를 분리한다.
    • 객체 구조의 요소들 위에서 수행되어지는 연산을 표현한다.
  • 특징
    • 기존 구조를 변경하지 않고 기존 클래스 계층구조에 새로운 행동들을 추가할 수 있도록 한다.
    • Visitor는 Component의 필드나 메서드를 사용할 수 있어야 하므로, 접근 권한을 더 많이 공개해야 할 수 있다.



개념적인 예시 #

// 컴포넌트 : 구조가 짜여져 있다. 
public interface IComponent
{
    void Accept(IVisitor visitor);
}

public class ConcreteComponentA : IComponent
{
    // 비지터를 받아들이고 
    // 비지터의 메서드A를 실행하게 한다.
    // 따라서 비지터는 컴포넌트A에 해당하는 동작을 처리한다. 
    public void Accept(IVisitor visitor)
    {
        visitor.VisitConcreteComponentA(this);
    }

    // 베이스 클래스에 없는 메서드도 있다. 
    // 하지만 비지터는 구체적인 컴포넌트를 알고 있으므로 이 메서드를 사용할 수 있다. 
    public string ExclusiveMethodOfConcreteComponentA()
    {
        return "A";
    }
}

public class ConcreteComponentB : IComponent
{
    public void Accept(IVisitor visitor)
    {
        visitor.VisitConcreteComponentB(this);
    }

    public string SpecialMethodOfConcreteComponentB()
    {
        return "B";
    }
}

// 비지터 : 컴포넌트를 방문해서 동작을 처리한다.
// 구체적인 컴포넌트 클래스에 따라 동작한다. 
// 새로운 처리를 추가하고 싶다면, 새로운 방문자를 만들고, 컴포넌트에서 방문자를 받아들이게 하면 된다. 
public interface IVisitor
{
    void VisitConcreteComponentA(ConcreteComponentA element);
    void VisitConcreteComponentB(ConcreteComponentB element);
}

class ConcreteVisitor1 : IVisitor
{
    public void VisitConcreteComponentA(ConcreteComponentA element)
    {
        Console.WriteLine("1-");
        Console.WriteLine(element.ExclusiveMethodOfConcreteComponentA());
    }

    public void VisitConcreteComponentB(ConcreteComponentB element)
    {
        Console.WriteLine("1-");
        Console.WriteLine(element.SpecialMethodOfConcreteComponentB());
    }
}

class ConcreteVisitor2 : IVisitor
{
    public void VisitConcreteComponentA(ConcreteComponentA element)
    {
        Console.WriteLine("2-");
        Console.WriteLine(element.ExclusiveMethodOfConcreteComponentA());
    }

    public void VisitConcreteComponentB(ConcreteComponentB element)
    {
        Console.WriteLine("2-");
        Console.WriteLine(element.SpecialMethodOfConcreteComponentB());
    }
}

public class Client
{
    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()
        };

        var visitor1 = new ConcreteVisitor1();
        var visitor2 = new ConcreteVisitor2();
        
        // 똑같은 클라이언트 코드로 다양한 비지터를 사용할 수 있다. 
        Client.ClientCode(components, visitor1); // 1-A, 1-B
        Client.ClientCode(components, visitor2); // 2-A, 2-B
    }
}



References #