โหมดผู้เยี่ยมชมใน Delphi จะขยายโหมดผู้เยี่ยมชมขั้นพื้นฐาน สำหรับข้อมูลเพิ่มเติมเกี่ยวกับโหมดผู้เยี่ยมชม โปรดดูที่ [Gam+, หน้า 331..344]
แสดงถึงการดำเนินการที่ดำเนินการกับองค์ประกอบที่ทำให้เป็นกลางของโครงสร้างวัตถุ ช่วยให้คุณสามารถกำหนดการดำเนินการใหม่ที่ดำเนินการกับแต่ละองค์ประกอบโดยไม่ต้องเปลี่ยนคลาส
[เกม+,หน้า331].
พิจารณาเครื่องมือสร้างแบบจำลองเชิงวัตถุ เช่น 'Rational Rose, ModelMaker' ซึ่งแสดงถึงโมเดลเป็นคลาสและสมาชิกของคลาส
เครื่องมือสร้างโมเดลมีฟังก์ชันมากมายสำหรับสมาชิกปฏิบัติการ เช่น แสดงรายการสมาชิกทั้งหมดของคลาส การสร้างโค้ดเฟรมเวิร์กของคลาส วิศวกรรมย้อนกลับ ฯลฯ
การดำเนินการเหล่านี้ส่วนใหญ่ดำเนินการที่แตกต่างกันกับสมาชิกที่แตกต่างกัน โดยแบ่งสมาชิกออกเป็นสาขา วิธีการ
คุณสมบัติ (คุณสมบัติ) ดังนั้นเราจึงต้องสร้างคลาสที่จัดการฟิลด์โดยเฉพาะ คลาสที่จัดการเมธอดโดยเฉพาะ และอื่นๆ แน่นอนว่าการรวบรวมคลาสของสมาชิกนั้นขึ้นอยู่กับภาษาที่กำลังรวบรวม แต่มีการเปลี่ยนแปลงไม่มากสำหรับภาษาที่กำหนด
รูปนี้แสดงกรอบการทำงานของคลาสสมาชิกบางคลาส ปัญหาเกิดขึ้น ถ้าฉันกระจายการดำเนินการเหล่านี้ทั้งหมดไปยังคลาสสมาชิกที่แตกต่างกัน
จะทำให้ระบบทั้งหมดเข้าใจ ปรับเปลี่ยน และบำรุงรักษาได้ยาก การรวมการสร้างรหัสชั้นเรียนร่วมกับการตรวจสอบสมาชิกชั้นเรียนทำให้เกิดความสับสน บางคลาสจำเป็นต้องคอมไพล์ใหม่เมื่อเพิ่มการดำเนินการใหม่ (อย่างน้อยระบบที่เกี่ยวข้องทั้งหมดก็ต้องคอมไพล์ใหม่เช่นกัน) มีวิธี: คุณสามารถเพิ่มการดำเนินการใหม่โดยไม่ขึ้นอยู่กับคลาสสมาชิกเป็นการดำเนินการที่ดำเนินการนั้น
เพื่อให้บรรลุเป้าหมายทั้งสองข้างต้น เราสามารถรวมการดำเนินการที่เกี่ยวข้องในแต่ละคลาสไว้ในอ็อบเจ็กต์อิสระ (เรียกว่า visitor )
และส่งวัตถุนี้ไปยังสมาชิกปัจจุบันเมื่อวนซ้ำรายชื่อสมาชิกของชั้นเรียน เมื่อสมาชิก 'ยอมรับ' การเข้าถึง สมาชิกจะส่งคำขอที่มีข้อมูลของตนเองไปยังผู้เยี่ยมชม สมาชิกใช้ตัวเองเป็นพารามิเตอร์ ผู้เยี่ยมชมดำเนินการเหล่านี้
ตัวอย่างเช่น: ตัวสร้างโค้ดที่ไม่ได้ใช้ผู้เยี่ยมชมอาจสร้างโค้ดผ่านวิธีนามธรรมของคลาสสมาชิก: TMember.WriteInterfaceCode(Output: TStream) สมาชิกแต่ละคนจะเรียก WriteInterfaceCode เพื่อสร้างโค้ดเอาต์พุตที่เหมาะสม หากรหัสถูกสร้างขึ้นผ่านผู้เยี่ยมชม ออบเจ็กต์ TinterfaceCodeVisitor จะถูกสร้างขึ้น และวิธีการ AcceptVisitor ที่มีพารามิเตอร์เป็นออบเจ็กต์การเข้าถึงจะถูกเรียกในรายชื่อสมาชิก สมาชิกแต่ละคนที่ใช้ AcceptVisitor จะเรียกกลับ ผู้เยี่ยมชม โดย ช่องจะเรียกวิธี VisitField ของผู้เยี่ยมชม และวิธีการจะเรียกวิธี VisitMethod ด้วยวิธีนี้ การดำเนินการ WriteInterfaceCode ของคลาสก่อนหน้า Tfield จะกลายเป็นการดำเนินการ VisitField ของ TinterfaceCodeVisitor
เพื่อให้ผู้เยี่ยมชมทำมากกว่าแค่การสร้างโค้ด เราต้องการให้ผู้เยี่ยมชมรายชื่อสมาชิกทุกคนต้องมีคลาสพาเรนต์ที่เป็นนามธรรม TmemberVisitor TmemberVisitor ต้องกำหนดวิธีการสำหรับสมาชิกแต่ละคน แอปพลิเคชันที่ต้องการส่งออกสมาชิกเป็นรูปแบบ HTML จะกำหนดคลาสย่อยใหม่ของ TmemberVisitor และไม่จำเป็นต้องเพิ่มโค้ดเฉพาะแอปพลิเคชันลงในคลาสสมาชิกอีกต่อไป รูปแบบผู้เยี่ยมชมสรุปการดำเนินการแต่ละรายการไว้ในผู้เยี่ยมชมที่เกี่ยวข้อง
การใช้รูปแบบ Visitor ต้องกำหนดคลาสสองระดับ: ระดับหนึ่งสอดคล้องกับองค์ประกอบที่ยอมรับการดำเนินการ (ระดับ Tmember) และอีกระดับหนึ่งกำหนดไว้สำหรับการดำเนินการกับองค์ประกอบ (ระดับ TmemberVisitor) เมื่อเพิ่มการดำเนินการใหม่ เพียงเพิ่มคลาสย่อยใหม่ให้กับลำดับชั้นของผู้เยี่ยมชม ฉันอาจกำหนดคลาสย่อย TmemberVisitor ใหม่เพื่อเพิ่มฟังก์ชันการทำงานใหม่
รหัสต่อไปนี้แสดงให้เห็นถึงการประยุกต์ใช้รูปแบบผู้เยี่ยมชมของคลาส Tmember ที่อธิบายไว้ข้างต้น
พิมพ์
TMember = คลาส (TObject)
สาธารณะ
ขั้นตอน AcceptMemberVisitor (ผู้เยี่ยมชม: TMemberVisitor เสมือน;
จบ;
TField = คลาส (TMember)
สาธารณะ
ขั้นตอน AcceptMemberVisitor (ผู้เยี่ยมชม: TMemberVisitor แทนที่;
จบ;
TMethod = คลาส (TMember)
สาธารณะ
ขั้นตอน AcceptMemberVisitor (ผู้เยี่ยมชม: TMemberVisitor แทนที่;
จบ;
TProperty = คลาส (TMember)
สาธารณะ
ขั้นตอน AcceptMemberVisitor (ผู้เยี่ยมชม: TMemberVisitor แทนที่;
จบ;
TMemberVisitor = คลาส (TObject)
สาธารณะ
ขั้นตอน VisitField (อินสแตนซ์: Tfield เสมือน;
ขั้นตอน VisitMember (อินสแตนซ์: TMember เสมือน);
ขั้นตอนการเยี่ยมชม (อินสแตนซ์: TMethod เสมือน);
ขั้นตอน VisitProperty (อินสแตนซ์: TProperty);
จบ;
การดำเนินการ
{สมาชิก}
เริ่ม
Visitor.VisitMember(ตนเอง);
จบ;
{สนาม}
ขั้นตอน TField.AcceptMemberVisitor (ผู้เยี่ยมชม: TMemberVisitor);
เริ่ม
จบ;
{ วิธีการ }
ขั้นตอน TMethod.AcceptMemberVisitor (ผู้เยี่ยมชม: TMemberVisitor);
เริ่ม
Visitor.VisitMethod(ตนเอง);
จบ;
{ทีพีพร็อพเพอร์ตี้}
ขั้นตอน TProperty.AcceptMemberVisitor (ผู้เยี่ยมชม: TMemberVisitor);
เริ่ม
Visitor.VisitProperty(ตนเอง);
จบ;
{ ผู้เยี่ยมชมสมาชิก }
ขั้นตอน TMemberVisitor.VisitField (อินสแตนซ์: TField);
เริ่ม
จบ;
ขั้นตอน TMemberVisitor.VisitMember (อินสแตนซ์: TMember);
เริ่ม
จบ;
ขั้นตอน TMemberVisitor.VisitMethod (อินสแตนซ์: TMethod);
เริ่ม
จบ;
ขั้นตอน TMemberVisitor.VisitProperty (อินสแตนซ์: TProperty);
เริ่ม
จบ;
แสดงให้เห็น:
? TMember, TField, TMethod และ Tproperty ทั้งหมดใช้เมธอด AcceptMemberVisitor
? คลาส TemberVisitor ใช้ VisitMember, VisitField และวิธีการอื่นๆ TmemberVisitor เป็นคลาสนามธรรม และวิธีการทั้งหมดถูกนำไปใช้โดยคลาสย่อยที่เป็นรูปธรรม
ด้านล่างนี้คือการใช้งานตัวสร้างโค้ดอย่างง่าย
การแนะนำรหัส:
• TCodeGenerationVisitor คือผู้เยี่ยมชมตัวสร้างโค้ดที่นำสมาชิกไปใช้
? ผู้เยี่ยมชมกำหนดคุณสมบัติตามบริบท: ผลลัพธ์: TTextStream,
จะต้องตั้งค่าก่อนที่จะเรียก VisitXXX ตัวอย่างเช่น: โดยทั่วไป DrawingVisitor ต้องใช้บริบทรวมถึง Canvas เพื่อรองรับการดำเนินการวาดภาพ บริบทถูกกำหนดให้กับตัวสร้างโค้ดก่อนที่จะวนซ้ำคู่สมาชิกทั้งหมด
? ตัวสร้างโค้ดจะรวมโค้ดทั้งหมดสำหรับคลาสที่สร้างขึ้น
เพื่อทำความเข้าใจโหมดผู้เยี่ยมชมอย่างแท้จริง คุณสามารถดำเนินการตามตัวอย่างนี้และเรียนรู้เพิ่มเติมเกี่ยวกับกลไกการจัดส่งแบบคู่: ยอมรับ/เยี่ยมชม
หน่วย CodeGenerators;
อินเตอร์เฟซ
ใช้คลาส, TextStreams;
พิมพ์
TCodeGenerator = คลาส (TObject)
สาธารณะ
ขั้นตอนการสร้าง (สมาชิก: TList; เอาต์พุต: TTextStream);
จบ;
การดำเนินการ
ใช้สมาชิก;
พิมพ์
TCodeGenerationVisitor = คลาส (TMemberVisitor)
ส่วนตัว
FOutput: TTextStream;
สาธารณะ
ขั้นตอน VisitField (อินสแตนซ์: Tfield แทนที่;
ขั้นตอน VisitMethod (อินสแตนซ์: TMethod);
ขั้นตอน VisitProperty (อินสแตนซ์: TProperty);
คุณสมบัติเอาท์พุท: TTextStream อ่าน FOutput เขียน FOutput;
จบ;
{TCodeGenerationVisitor}
ขั้นตอน TCodeGenerationVisitor.VisitField (อินสแตนซ์: TField);
เริ่ม
Output.WriteLnFmt(' %s: %s;', [Instance.Name, Instance.DataName]);
จบ;
ขั้นตอน TCodeGenerationVisitor.VisitMethod (อินสแตนซ์: TMethod);
var
MKStr, DTStr: สตริง;
เริ่ม
case Instance.MethodKind of
mkConstructor: MKStr := 'ตัวสร้าง';
mkDestructor: MKStr := 'ตัวทำลาย';
mkProcedure: MKStr := 'ขั้นตอน';
mkFuntion: MKStr := 'ฟังก์ชั่น';
จบ;
ถ้า Instance.MethodKind = mkFunction แล้ว
DTStr := ': ' + อินสแตนซ์ DataName
อื่น
DTStr := ';
{รหัสไม่สมบูรณ์ ก็เพียงพอที่จะสาธิตการสร้างรหัส Tmethod}
Output.WriteLnFmt(' %s %s%s%s;'
[MKStr, อินสแตนซ์.ชื่อ, อินสแตนซ์.พารามิเตอร์, DTStr]);
จบ;
ขั้นตอน TCodeGenerationVisitor.VisitProperty (อินสแตนซ์: TProperty);
เริ่ม
Output.WriteLnFmt(' คุณสมบัติ %s: %s อ่าน %s เขียน %s;',
[อินสแตนซ์.ชื่อ อินสแตนซ์.DataName,
Instance.ReadSpecifier, Instance.WriteSpecifier]);
จบ;
{TCodeGenerator}
ขั้นตอน TCodeGenerator.Generate (สมาชิก: TList; เอาต์พุต: TTextStream);
var
ฉัน: จำนวนเต็ม;
เริ่ม
{เขียนคำจำกัดความของชั้นเรียน}
Output.WriteLine('TSample = class (TObject)');
{ดี! ผู้เยี่ยมชมที่เข้าร่วมโปรแกรมสร้างโค้ด}
ผู้เยี่ยมชม := TCodeGenerationVisitor.Create;
พยายาม
{อย่าลืมระบุบริบทสำหรับการเข้าชมทั้งหมดเพื่อการเข้าถึงวิธี VisitXXX ได้ดียิ่งขึ้น -
สำหรับฉัน := 0 ถึง Members.Count - 1 do
{ส่วนเฉพาะของโค้ดที่มีสิ่งดีๆ เกิดขึ้น}
TMember(สมาชิก[I]).AcceptMemberVisitor(ผู้เยี่ยมชม);
ในที่สุด
ผู้เยี่ยมชมฟรี;
จบ;
{การสร้างโค้ดสำหรับสมาชิกชั้นเรียนเสร็จสมบูรณ์}
Output.WriteLine('end;');
จบ;
การจัดระเบียบ
// ข้อความที่ตัดตอนมาจาก "รูปแบบการออกแบบ" มากมาย