방문자 패턴 이해


16

GUI 컨트롤을 나타내는 클래스 계층이 있습니다. 이 같은:

Control->ContainerControl->Form

다양한 작업을 수행하는 객체에서 작동하는 일련의 알고리즘을 구현해야하며 방문자 패턴이 가장 깨끗한 솔루션이라고 생각합니다. 객체 계층의 Xml 표현을 만드는 알고리즘을 예로 들어 보겠습니다. '클래식'접근법을 사용하면 다음과 같이 할 수 있습니다.

public abstract class Control
{
    public virtual XmlElement ToXML(XmlDocument document)
    {
        XmlElement xml = document.CreateElement(this.GetType().Name);
        // Create element, fill it with attributes declared with control
        return xml;
    }
}

public abstract class ContainerControl : Control
{
    public override XmlElement ToXML(XmlDocument document)
    {
        XmlElement xml = base.ToXML(document);
        // Use forech to fill XmlElement with child XmlElements
        return xml;
    }
}

public class Form : ContainerControl
{
    public override XmlElement ToXML(XmlDocument document)
    {
        XmlElement xml = base.ToXML(document);
        // Fill remaining elements declared in Form class
        return xml;
    }
}

그러나 방문자 패턴 으로이 작업을 수행하는 방법을 잘 모르겠습니다. 이것이 기본 구현입니다.

public class ToXmlVisitor : IVisitor
{
    public void Visit(Form form)
    {
    }
}

추상 클래스조차도 구현에 도움이되므로 ToXmlVisitor에서 올바르게 수행하는 방법을 모르겠습니까?

방문자 패턴을 고려하는 이유는 일부 알고리즘에는 클래스가 구현되는 프로젝트에서 사용할 수없는 참조가 필요하고 많은 다른 알고리즘이 있으므로 큰 클래스를 피하기 때문입니다.


귀하의 질문은 무엇인가?
— gnat

기본적으로 방문자 패턴을 사용하여 ToXml () 메서드를 다시 작성하는 방법
— Nezreli


링크 주셔서 감사합니다. 동적 디스패치는 기존 방문자 패턴을 단순화하지만 크게 변경되지는 않습니다.
— Nezreli

@Nezreli 그렇습니다. 처리하는 Windows Forms 컨트롤과 같이 방문자 패턴을 지원하지 않는 클래스와 함께 작동합니다.
— Kris Vandermotten

답변:


17

방문자 패턴은 단일 바인딩 만 지원하는 프로그래밍 언어에서 이중 바인딩을 시뮬레이션하는 메커니즘입니다. 불행히도 그 진술은 많은 것을 명확하게하지 않을 수 있으므로 간단한 예를 들어 설명하겠습니다.

.NET 및 C #에서 사용중인 플랫폼 인 개체를 사용하여 문자열로 변환 할 수 있습니다. ToString() 함수를 . 해당 funtion이 수행하는 작업, 즉 실행중인 코드는 적용하는 객체 유형에 따라 다릅니다 (가상 방법 임). 어떤 코드가 실행되는지는 객체의 한 유형에 따라 다르므로 사용되는 메커니즘을 단일 바인딩이라고합니다.

그러나 객체마다 문자열을 변환하는 여러 가지 방법을 원한다면 어떻게해야합니까? 객체를 문자열로 변환하는 두 가지 방법을 원한다면 실행되는 코드가 두 가지에 달려 있습니다. 변환 할 객체뿐만 아니라 변환하려는 방식도 무엇입니까?

이중 바인딩이 있으면 멋지게 해결할 수 있습니다. 그러나 C #을 포함한 대부분의 OO 언어는 단일 바인딩 만 지원합니다.

방문자 패턴은 이중 바인딩을 두 개의 성공적인 단일 바인딩으로 전환하여 문제를 해결합니다.

위의 예에서, 변환 알고리즘을 구현하는 오브젝트에서 두 번째 가상 메소드를 호출하는 변환 할 오브젝트에서 가상 메소드를 사용합니다.

그러나 이는 알고리즘을 적용하려는 객체가 이것과 공동 작업을해야한다는 것을 의미합니다. 방문자 패턴을 지원해야합니다.

방문자 패턴을 지원하지 않는 .NET의 Windows Forms 클래스를 사용하고있는 것 같습니다. 보다 구체적으로, 그들은 가지고 public virtual void Accept(IVisitor)있지 않은 방법 이 필요합니다 .

대체 대안은 무엇입니까? .NET은 단일 바인딩을 지원할뿐만 아니라 동적 바인딩도 지원하므로 이중 바인딩보다 훨씬 약합니다.

문제를 해결할 수있는 해당 기술을 적용하는 방법에 대한 자세한 내용은 잘 이해하는 경우 Farewell Visitor를 참조하십시오 .

최신 정보:

특정 문제에 기술을 적용하려면 먼저 확장 방법을 정의하십시오.

public static XmlDocument ToXml(this Control control)
{
    XmlDocument xml = new XmlDocument();
    XmlElement root = xml.CreateElement(control.GetType().Name);
    xml.AppendChild(root);

    Visit(control, xml, root);

    return xml;
}

동적 디스패처를 작성하십시오.

private static void Visit(Control control, XmlDocument xml, XmlElement root)
{
    dynamic dynamicControl = control; //notice the 'dynamic' type.
                                      //this is the key to dynamic dispatch

    VisitCore(dynamicControl, xml, root);
}

그런 다음 특정 방법을 작성하십시오.

private static void VisitCore(Control control, XmlDocument xml, XmlElement root)
{
    // TODO: specific Control handling
}

private static void VisitCore(ContainerControl control, XmlDocument xml, XmlElement root)
{
    // call the "base" method
    VisitCore(control as Control, xml, root);

    // TODO: specific ContainerControl handling
    // for example:
    foreach (Control child in control.Controls)
    {
        XmlElement element = xml.CreateElement(child.GetType().Name);
        root.AppendChild(element);

        // call the dynamic dispatcher method
        Visit(child, xml, element);
    }
}

private static void VisitCore(Form control, XmlDocument xml, XmlElement root)
{
    // call the "base" method
    VisitCore(control as ContainerControl, xml, root);

    // TODO: specific Form handling
}

.NET의 동적 디스패치는 실제로 매우 강력합니다 .. 그러나 약간 느릴 수 있음을 알았습니다 ... 느리지 만 단일 클래스의 코드에서 여러 클래스의 sevral 라인과 방문자와의 인터페이스를
— 취하는 것

여전히 ToXml 알고리즘을 사용하면 상속 체인을 통해 모든 유형을 '방문'해야하므로 동적 디스패치로 문제가 해결되지 않습니다. 내 예제에서는 성공적인 XML 변환을 위해 Control, ContainterControl 및 Form을 순서대로 방문해야합니다.
— Nezreli

@ Nezreli 문제를 해결할 수 있습니다. 방법을 보여주기 위해 답변을 업데이트했습니다.
— Kris Vandermotten

동적 변수 정의에 주석을 추가 할 수있는 권한을 가졌습니다. 그것을 발견하기 전에 코드를 두 번 읽었으며 전체 이야기의 핵심입니다.
— Cristi Diaconescu
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.