يعمل وضع الزائر في دلفي على توسيع وضع الزائر الأساسي. لمزيد من المعلومات حول وضع الزائر، يرجى الرجوع إلى [Gam+، الصفحات 331..344].
يمثل عملية تعمل على العناصر المحايدة لبنية الكائن. يسمح لك بتحديد العمليات الجديدة التي تعمل على كل عنصر دون تغيير فئته.
[غام+، صفحة 331].
خذ بعين الاعتبار أداة النمذجة الموجهة للكائنات، مثل "Rational Rose, ModelMaker"، والتي تمثل النموذج كفئات وأعضاء فئة.
توفر أداة النمذجة العديد من الوظائف للأعضاء العاملين، مثل: سرد جميع أعضاء الفصل، وإنشاء إطار التعليمات البرمجية للفئة، والهندسة العكسية، وما إلى ذلك.
معظم هذه العمليات تؤدي عمليات مختلفة على أعضاء مختلفة. ويقسم الأعضاء إلى مجالات، وأساليب،
خصائص (خصائص). لذلك، يجب علينا إنشاء فئات تتعامل مع الحقول على وجه التحديد، وفئات تتعامل مع الأساليب على وجه التحديد، وما إلى ذلك. تعتمد مجموعة فئات الأعضاء بالطبع على اللغة التي يتم تجميعها. ولكن ليس هناك الكثير من التغييرات للغة معينة.
يوضح الشكل إطار عمل بعض فئات الأعضاء. تنشأ المشكلة، إذا قمت بنشر كل هذه العمليات على فئات أعضاء مختلفة،
سيجعل من الصعب فهم النظام بأكمله وتعديله وصيانته. يؤدي إنشاء كود الفصل مع فحص أعضاء الفصل إلى حدوث ارتباك. تحتاج بعض الفئات إلى إعادة الترجمة عند إضافة عمليات جديدة (على الأقل يجب إعادة ترجمة جميع الأنظمة ذات الصلة أيضًا). هناك طريقة: يمكنك إضافة عملية جديدة بشكل مستقل عن فئة العضو باعتبارها العملية التي تعمل عليها.
لتحقيق الهدفين المذكورين أعلاه، يمكننا تغليف العمليات ذات الصلة في كل فئة في كائن مستقل (يسمى الزائر )
وقم بتمرير هذا الكائن إلى العضو الحالي عند التكرار عبر قائمة أعضاء الفصل. عندما "يقبل" أحد الأعضاء الوصول، يرسل العضو طلبًا يحتوي على معلوماته الخاصة إلى الزائر. يأخذ العضو نفسه كمعلمة. يقوم الزوار بهذه الإجراءات.
على سبيل المثال: قد يقوم منشئ التعليمات البرمجية الذي لا يستخدم الزوار بإنشاء تعليمات برمجية من خلال الطريقة المجردة لفئة العضو: TMember.WriteInterfaceCode(Output: TStream). سيقوم كل عضو باستدعاء WriteInterfaceCode لإنشاء رمز الإخراج المناسب. إذا تم إنشاء التعليمات البرمجية من خلال زائر، فسيتم إنشاء كائن TinterfaceCodeVisitor وسيتم استدعاء أسلوب AcceptVisitor مع المعلمة التي تمثل كائن الوصول في قائمة الأعضاء. سيقوم كل عضو ينفذ AcceptVisitor بمعاودة الاتصال بالزائر : سيستدعي الحقل أسلوب VisitField الخاص بالزائر، وسيقوم الأسلوب باستدعاء أسلوب VisitMethod. بهذه الطريقة، تصبح عملية WriteInterfaceCode للفئة السابقة Tfield الآن عملية VisitField الخاصة بـ TinterfaceCodeVisitor.
لجعل الزائر يفعل أكثر من مجرد إنشاء التعليمات البرمجية، نحتاج إلى أن يكون لدى جميع زوار قائمة الأعضاء فئة أصل مجردة TmemberVisitor. يجب على TmemberVisitor تحديد طريقة لكل عضو. سيحدد التطبيق الذي يحتاج إلى إخراج الأعضاء إلى تنسيق HTML فئة فرعية جديدة من TmemberVisitor ولن يحتاج بعد الآن إلى إضافة تعليمات برمجية خاصة بالتطبيق إلى فئة العضو. يقوم نمط الزائر بتغليف كل عملية في زائر ذي صلة
باستخدام نمط الزائر، يجب تحديد مستويين من الفئات: أحدهما يتوافق مع العناصر التي تقبل العمليات (مستوى Tmember) والآخر محدد للعمليات على العناصر (مستوى TmemberVisitor). عند إضافة عملية جديدة، ما عليك سوى إضافة فئة فرعية جديدة إلى التسلسل الهرمي للزائرين. ربما يمكنني ببساطة تحديد فئة فرعية جديدة لـ TmemberVisitor لإضافة وظائف جديدة.
يوضح التعليمة البرمجية التالية تطبيق نمط الزائر من فئة T member الموصوفة أعلاه.
يكتب
TMember = الفئة (TObject)
عام
الإجراء AcceptMemberVisitor(Visitor: TMemberVisitor);
نهاية؛
TField = الفئة (TMember)
عام
الإجراء AcceptMemberVisitor(Visitor: TMemberVisitor);
نهاية؛
TMethod = الفئة (TMember)
عام
الإجراء AcceptMemberVisitor(Visitor: TMemberVisitor);
نهاية؛
TProperty = الفئة (TMember)
عام
الإجراء AcceptMemberVisitor(Visitor: TMemberVisitor);
نهاية؛
TMemberVisitor = الفئة (TObject)
عام
الإجراء VisitField(Instance: TField virtual);
الإجراء VisitMember(Instance: TMember);
الإجراء VisitMethod(Instance: TMethod);
الإجراء VisitProperty(Instance: TProperty virtual);
نهاية؛
تطبيق
{عضو}
يبدأ
Visitor.VisitMember(Self);
نهاية؛
{تفيلد}
الإجراء TField.AcceptMemberVisitor(Visitor: TMemberVisitor);
يبدأ
نهاية؛
{ الطريقة }
الإجراء TMethod.AcceptMemberVisitor(Visitor: TMemberVisitor);
يبدأ
Visitor.VisitMethod(Self);
نهاية؛
{TPProperty}
الإجراء TProperty.AcceptMemberVisitor(Visitor: TMemberVisitor);
يبدأ
Visitor.VisitProperty(Self);
نهاية؛
{ عضو زائر }
الإجراء TMemberVisitor.VisitField(Instance: TField);
يبدأ
نهاية؛
الإجراء TMemberVisitor.VisitMember(Instance: TMember);
يبدأ
نهاية؛
الإجراء TMemberVisitor.VisitMethod(Instance: TMethod);
يبدأ
نهاية؛
الإجراء TMemberVisitor.VisitProperty(Instance: TProperty);
يبدأ
نهاية؛
يوضح:
يقوم كل من TMember وTField وTMethod وTproperty بتطبيق أسلوب AcceptMemberVisitor
تطبق فئة TMemberVisitor أساليب VisitMember وVisitField وغيرها. TmemberVisitor هي فئة مجردة، ويتم تنفيذ جميع أساليبها من خلال فئات فرعية محددة.
يوجد أدناه تطبيق لمولد أكواد بسيط.
مقدمة الكود:
• TCodeGenerationVisitor هو زائر لمولد الكود الذي ينفذ العضو.
يحدد الزائر خاصية حساسة للسياق: الإخراج: TTextStream،
يجب تعيينه قبل استدعاء VisitXXX على سبيل المثال: يتطلب DrawVisitor عادةً سياقًا يتضمن اللوحة القماشية لدعم عمليات الرسم. يتم تعيين السياق لمولد الكود قبل التكرار عبر زوج الأعضاء بأكمله.
سيقوم منشئ الكود بدمج كافة التعليمات البرمجية للفئة التي تم إنشاؤها
لفهم وضع الزائر حقًا، يمكنك تنفيذ هذا المثال ومعرفة آلية الإرسال المزدوج: قبول/زيارة.
مولدات كود الوحدة؛
واجهة
يستخدم فئات، TextStreams؛
يكتب
TCodeGenerator = الفئة (TObject)
عام
إنشاء الإجراء (الأعضاء: TList؛ الإخراج: TTextStream)؛
نهاية؛
تطبيق
يستخدم الأعضاء؛
يكتب
TCodeGenerationVisitor = الفئة (TMemberVisitor)
خاص
الإخراج: TTextStream؛
عام
الإجراء VisitField(Instance: TField);
الإجراء VisitMethod(Instance: TMethod);
الإجراء VisitProperty(Instance: TProperty);
إخراج الخاصية: قراءة TTextStream FOutput والكتابة FOutput؛
نهاية؛
{TCodeGenerationVisitor}
الإجراء TCodeGenerationVisitor.VisitField(Instance: TField);
يبدأ
Output.WriteLnFmt(' %s: %s;', [Instance.Name, Instance.DataName]);
نهاية؛
الإجراء TCodeGenerationVisitor.VisitMethod(Instance: TMethod);
فار
MKStr، DTStr: سلسلة؛
يبدأ
حالة المثيل.الطريقةنوع من
mkConstructor: MKStr := 'constructor';
mkDestructor: MKStr := 'destructor';
mkProcedure: MKStr := 'procedure';
mkFuntion: MKStr := 'function';
نهاية؛
إذا كان Instance.MethodKind = mkFunction إذن
DTStr := ': ' + Instance.DataName
آخر
DTStr := ';
{الكود غير مكتمل، يكفي توضيح طريقة إنشاء الكود}
Output.WriteLnFmt(' %s %s%s%s;'
[MKStr، Instance.Name، Instance.Parameters، DTStr])؛
نهاية؛
الإجراء TCodeGenerationVisitor.VisitProperty(Instance: TProperty);
يبدأ
Output.WriteLnFmt(' الخاصية %s: %s قراءة %s كتابة %s;',
[اسم المثيل، اسم المثيل، اسم البيانات،
Instance.ReadSpecifier, Instance.WriteSpecifier]);
نهاية؛
{تكودجينيراتور}
الإجراء TCodeGenerator.Generate(الأعضاء: TList; الإخراج: TTextStream);
فار
أنا: عدد صحيح؛
يبدأ
{اكتب تعريف الفصل}
Output.WriteLine('TSample = class (TObject)');
{جيد! الزائرون الذين انضموا إلى منشئ الأكواد}
الزائر := TCodeGenerationVisitor.Create;
يحاول
{تذكر توفير السياق لجميع الزيارات للوصول بشكل أفضل إلى طرق VisitXXX. }
لأني := 0 إلى Members.Count - 1 do
{القسم المحدد من التعليمات البرمجية الذي حدث فيه شيء جيد}
TMember(الأعضاء[I]).AcceptMemberVisitor(Visitor);
أخيراً
زائر مجاني؛
نهاية؛
{اكتمل إنشاء التعليمات البرمجية لأعضاء الفصل}
Output.WriteLine('end;');
نهاية؛
تنظيم
// مقتطفات كثيرة من "أنماط التصميم"،