ทำความเข้าใจกับรูปแบบของผู้มาเยี่ยม


16

ฉันมีลำดับชั้นของชั้นเรียนที่แสดงถึงการควบคุม GUI บางสิ่งเช่นนี้

Control->ContainerControl->Form

ฉันต้องใช้ชุดของ algoritms ที่ทำงานกับวัตถุที่ทำสิ่งต่าง ๆ และฉันคิดว่ารูปแบบของผู้เข้าชมจะเป็นวิธีที่สะอาดที่สุด ลองยกตัวอย่างเช่นอัลกอริทึมที่สร้าง Xml Representaion ของลำดับชั้นของวัตถุ ใช้วิธี 'คลาสสิค' ฉันจะทำสิ่งนี้:

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 ได้อย่างไร

เหตุผลที่ฉันพิจารณารูปแบบของผู้เยี่ยมชมคืออัลกอริธึมบางอย่างจะต้องมีการอ้างอิงที่ไม่มีในโครงการที่มีการใช้คลาสและมีอัลกอริทึมที่แตกต่างกันจำนวนหนึ่งดังนั้นฉันจึงหลีกเลี่ยงคลาสที่มีขนาดใหญ่


คำถามของคุณคืออะไร?
ริ้น

โดยทั่วไปวิธีการเขียนวิธี ToXml () โดยใช้รูปแบบผู้เยี่ยมชม
Nezreli


ขอบคุณสำหรับลิงค์ การจัดส่งแบบไดนามิกช่วยลดความซับซ้อนของรูปแบบผู้เข้าชมดั้งเดิม แต่ไม่เปลี่ยนแปลงมากนัก
Nezreli

@Nezreli ใช่แล้ว ใช้งานได้กับคลาสที่ไม่รองรับรูปแบบผู้เยี่ยมชมเช่นตัวควบคุม Windows Forms ที่คุณกำลังติดต่อด้วย
Kris Vandermotten

คำตอบ:


17

รูปแบบผู้เยี่ยมชมเป็นกลไกในการจำลองการเชื่อมโยงคู่ในภาษาการเขียนโปรแกรมที่รองรับการเชื่อมเดี่ยวเท่านั้น น่าเสียดายที่ข้อความนั้นอาจไม่กระจ่างอะไรมากมายดังนั้นให้ฉันอธิบายด้วยตัวอย่างง่ายๆ

ใน. NET และ C # ซึ่งเป็นแพลตฟอร์มที่คุณใช้วัตถุสามารถแปลงเป็นสตริงโดยใช้ToString()ฟังก์ชัน สิ่งที่ funtion ทำคือรหัสที่ถูกเรียกใช้ขึ้นอยู่กับประเภทของวัตถุที่คุณใช้กับมัน (เป็นวิธีเสมือน) รหัสใดที่ถูกดำเนินการขึ้นอยู่กับสิ่งหนึ่งสิ่งหนึ่งชนิดของวัตถุดังนั้นกลไกที่ใช้เรียกว่าการรวมเดี่ยว

แต่ถ้าฉันต้องการมีมากกว่าหนึ่งวิธีในการแปลงวัตถุเป็นสตริงสำหรับวัตถุแต่ละชนิด ถ้าฉันต้องการมีสองวิธีในการแปลงออบเจ็กต์เป็นสตริงเพื่อให้โค้ดที่ถูกประมวลผลนั้นขึ้นอยู่กับสองสิ่ง: ไม่เพียง แต่ออบเจ็กต์ที่จะแปลง แต่ยังเป็นวิธีที่เราต้องการให้แปลง

นั่นอาจแก้ไขได้หากเรามีการผูกสองหน้า แต่ภาษา OO ส่วนใหญ่รวมถึง C # รองรับการเชื่อมโยงเดียวเท่านั้น

รูปแบบผู้มาเยือนแก้ปัญหาได้โดยเปลี่ยนการผูกสองเป็นสองการผูกเดียวที่ประสบความสำเร็จ

ในตัวอย่างของเราข้างต้นมันจะใช้วิธีเสมือนในวัตถุที่จะแปลงซึ่งเรียกวิธีเสมือนที่สองในวัตถุที่ใช้อัลกอริทึมการแปลง

แต่นั่นก็หมายความว่าวัตถุที่คุณต้องการใช้อัลกอริทึมนั้นจำเป็นต้องร่วมมือกับสิ่งนี้: มันต้องมีการรองรับรูปแบบของผู้มาเยี่ยม

ดูเหมือนว่าคุณจะใช้คลาส Windows Forms ของ. NET ซึ่งไม่สนับสนุนรูปแบบผู้เยี่ยมชม โดยเฉพาะพวกเขาจะต้องมีpublic virtual void Accept(IVisitor)วิธีการซึ่งแน่นอนว่าพวกเขาไม่มี

ดังนั้นทางเลือกคืออะไร . NET ไม่เพียง แต่รองรับการเชื่อมโยงเดียว แต่ยังรองรับการเชื่อมโยงแบบไดนามิกซึ่งยิ่งมีประสิทธิภาพยิ่งกว่าการเชื่อมโยงคู่

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการใช้เทคนิคที่ซึ่งจะช่วยให้คุณสามารถแก้ปัญหาของคุณ (ถ้าผมเข้าใจมันได้ดี) จะดูที่อำลาผู้เข้าชม

UPDATE:

หากต้องการใช้เทคนิคกับปัญหาเฉพาะของคุณอันดับแรกให้กำหนดวิธีการขยายของคุณ:

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 นั้นค่อนข้างมีประสิทธิภาพ .. แต่ฉันสังเกตเห็นว่ามันอาจเป็นบิต ... ดี .. ช้า แต่มันทำในบรรทัดเดียวของรหัสสิ่งที่ต้องใช้เส้นเจ็ดในหลายชั้นเรียนและอินเตอร์เฟซกับผู้เข้าชม
Newtopian

ยังคงการจัดส่งแบบไดนามิกจะไม่แก้ปัญหาของฉันเพราะอัลกอริทึม ToXml ของฉันต้องการให้ฉัน 'เยี่ยมชม' ทุกประเภทขึ้นห่วงโซ่มรดก ในตัวอย่างของฉันต้องไปที่การควบคุม ContainterControl และแบบฟอร์มเพื่อให้การแปลง XML ประสบความสำเร็จ
Nezreli

@Nezreli มันสามารถแก้ปัญหาของคุณได้ฉันได้อัปเดตคำตอบของฉันเพื่อแสดงให้คุณเห็นว่า
Kris Vandermotten

ฉันได้รับอนุญาตให้เพิ่มความคิดเห็นในนิยามตัวแปรแบบไดนามิก ฉันอ่านโค้ดสองฉบับก่อนที่ฉันจะเห็นมันและมันคือกุญแจสำคัญของเรื่องราวทั้งหมด
Cristi Diaconescu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.