---
title: "الزائر"
type: "design-pattern"
slug: "visitor"
url: "http://localhost:3000/ar/design-patterns/visitor.md"
category: "الأنماط السلوكية"
description: "الزائر هو نمط تصميم سلوكي يتيح لك فصل الخوارزميات عن الكائنات التي تعمل عليها."
languages: ["java", "csharp", "cpp", "go", "php", "python", "ruby", "rust", "swift", "typescript"]
---
# الزائر

> الزائر هو نمط تصميم سلوكي يتيح لك فصل الخوارزميات عن الكائنات التي تعمل عليها.

## Intent

**الزائر** هو نمط تصميم سلوكي يتيح لك فصل الخوارزميات عن الكائنات التي تعمل عليها.

## Problem

تخيّل أن فريقك يطوّر تطبيقاً يعمل مع معلومات جغرافية منظّمة على شكل رسم بياني ضخم. قد تمثّل كل عقدة في الرسم البياني كياناً معقداً كمدينة، أو أشياء أكثر تفصيلاً كالصناعات ومناطق المعالم السياحية، وما إلى ذلك. ترتبط العقد بغيرها إذا كان هناك طريق بين الأشياء الحقيقية التي تمثّلها. في الخلفية، يُمثَّل كل نوع من العقد بفئته الخاصة، بينما كل عقدة محددة هي كائن.

تصدير الرسم البياني إلى XML.

في مرحلة ما، أُسنِدت إليك مهمة تنفيذ تصدير الرسم البياني إلى تنسيق XML. في البداية، بدت المهمة بسيطة للغاية. خططت لإضافة أسلوب تصدير إلى كل فئة من فئات العقد، ثم الاستفادة من التكرار العودي لاجتياز كل عقدة في الرسم البياني وتنفيذ أسلوب التصدير. كان الحل بسيطاً وأنيقاً: بفضل تعدد الأشكال، لم تكن تقرن الكود الذي يستدعي أسلوب التصدير بالفئات الملموسة للعقد.

لسوء الحظ، رفض مهندس النظام السماح لك بتعديل فئات العقد الموجودة. قال إن الكود كان في الإنتاج بالفعل ولا يريد المخاطرة بكسره بسبب خطأ محتمل في تغييراتك.

كان لا بد من إضافة أسلوب تصدير XML إلى جميع فئات العقد، مما ينطوي على خطر كسر التطبيق بأكمله إذا تسرّب أي خطأ مع التغيير.

علاوة على ذلك، تساءل عمّا إذا كان من المنطقي وجود كود تصدير XML داخل فئات العقد. كانت الوظيفة الأساسية لهذه الفئات هي العمل مع البيانات الجغرافية. سيبدو سلوك تصدير XML غريباً هناك.

كان هناك سبب آخر للرفض. كان من المرجح جداً أنه بعد تنفيذ هذه الميزة، سيطلب منك شخص ما من قسم التسويق توفير القدرة على التصدير إلى تنسيق مختلف، أو طلب أشياء غريبة أخرى. سيضطرك هذا إلى تغيير تلك الفئات القيّمة والهشّة مرة أخرى.

## Solution

يقترح نمط الزائر وضع السلوك الجديد في فئة منفصلة تُسمى _visitor_، بدلاً من محاولة دمجه في الفئات الموجودة. يُمرَّر الكائن الأصلي الذي كان عليه تنفيذ السلوك الآن إلى أحد أساليب الزائر كوسيطة، مما يتيح للأسلوب الوصول إلى جميع البيانات اللازمة الموجودة في الكائن.

الآن، ماذا لو كان يمكن تنفيذ هذا السلوك على كائنات من فئات مختلفة؟ على سبيل المثال، في حالتنا مع تصدير XML، سيكون التنفيذ الفعلي مختلفاً قليلاً عبر فئات العقد المختلفة. وبالتالي، قد تُعرِّف فئة الزائر ليس أسلوباً واحداً، بل مجموعة من الأساليب، يمكن لكل منها أن يأخذ وسيطات من أنواع مختلفة، كما يلي:

class ExportVisitor implements Visitor is
    method doForCity(City c) { ... }
    method doForIndustry(Industry f) { ... }
    method doForSightSeeing(SightSeeing ss) { ... }
    // ...

ولكن كيف بالضبط سنستدعي هذه الأساليب، خاصة عند التعامل مع الرسم البياني بأكمله؟ تمتلك هذه الأساليب توقيعات مختلفة، لذا لا يمكننا استخدام تعدد الأشكال. لاختيار أسلوب الزائر المناسب القادر على معالجة كائن معين، سنحتاج إلى التحقق من فئته. أليس هذا كابوساً؟

foreach (Node node in graph)
    if (node instanceof City)
        exportVisitor.doForCity((City) node)
    if (node instanceof Industry)
        exportVisitor.doForIndustry((Industry) node)
    // ...
}

قد تسأل، لماذا لا نستخدم التحميل الزائد للأساليب؟ هذا عندما تعطي جميع الأساليب نفس الاسم، حتى لو كانت تدعم مجموعات مختلفة من المعاملات. لسوء الحظ، حتى لو افترضنا أن لغة البرمجة تدعمه أصلاً (كما تفعل Java و C#)، فلن يفيدنا ذلك. نظراً لأن الفئة الدقيقة لكائن العقدة غير معروفة مسبقاً، لن تتمكن آلية التحميل الزائد من تحديد الأسلوب الصحيح للتنفيذ. ستعود بشكل افتراضي إلى الأسلوب الذي يأخذ كائناً من الفئة الأساسية `Node`.

ومع ذلك، يعالج نمط الزائر هذه المشكلة. يستخدم تقنية تُسمى [الإرسال المزدوج](/ar/design-patterns/visitor-double-dispatch)، والتي تساعد على تنفيذ الأسلوب الصحيح على كائن دون شروط مرهقة. بدلاً من السماح للعميل باختيار الإصدار المناسب من الأسلوب للاستدعاء، كيف لو فوّضنا هذا الاختيار إلى الكائنات التي نمررها إلى الزائر كوسيطة؟ بما أن الكائنات تعرف فئاتها الخاصة، فستتمكن من اختيار الأسلوب المناسب على الزائر بأقل إحراج. إنها "تقبل" زائراً وتخبره بأسلوب الزيارة الذي يجب تنفيذه.

// كود العميل
foreach (Node node in graph)
    node.accept(exportVisitor)

// المدينة
class City is
    method accept(Visitor v) is
        v.doForCity(this)
    // ...

// الصناعة
class Industry is
    method accept(Visitor v) is
        v.doForIndustry(this)
    // ...

أعترف. كان علينا تغيير فئات العقد في النهاية. لكن على الأقل التغيير بسيط ويتيح لنا إضافة سلوكيات أخرى دون تعديل الكود مرة أخرى.

الآن، إذا استخرجنا واجهة مشتركة لجميع الزوار، ستتمكن جميع العقد الموجودة من العمل مع أي زائر تُدخله إلى التطبيق. إذا وجدت نفسك تُدخل سلوكاً جديداً مرتبطاً بالعقد، كل ما عليك فعله هو تنفيذ فئة زائر جديدة.

## Structure

1. تُعلن واجهة **الزائر** عن مجموعة من أساليب الزيارة التي يمكنها أخذ عناصر ملموسة من هيكل الكائن كوسيطات. قد تحمل هذه الأساليب نفس الأسماء إذا كان البرنامج مكتوباً بلغة تدعم التحميل الزائد، لكن يجب أن يكون نوع معاملاتها مختلفاً.
2. يُنفِّذ كل **زائر ملموس** عدة إصدارات من نفس السلوكيات، مُخصَّصة لفئات عنصر ملموسة مختلفة.
3. تُعلن واجهة **العنصر** عن أسلوب "لقبول" الزوار. يجب أن يحتوي هذا الأسلوب على معامل واحد مُعلَن بنوع واجهة الزائر.
4. يجب أن يُنفِّذ كل **عنصر ملموس** أسلوب القبول. الغرض من هذا الأسلوب هو إعادة توجيه الاستدعاء إلى الأسلوب الصحيح للزائر المقابل لفئة العنصر الحالي. انتبه إلى أنه حتى لو نفّذت فئة العنصر الأساسية هذا الأسلوب، فلا يزال يجب على جميع الفئات الفرعية تجاوز هذا الأسلوب في فئاتها الخاصة واستدعاء الأسلوب المناسب على كائن الزائر.
5. يمثّل **العميل** عادةً مجموعة أو أي كائن معقد آخر (على سبيل المثال، شجرة [مركّب](/ar/design-patterns/composite)). عادةً لا يكون العملاء على دراية بجميع فئات العنصر الملموسة لأنهم يعملون مع الكائنات من تلك المجموعة عبر واجهة مجردة.

## Pseudocode

في هذا المثال، يُضيف نمط **الزائر** دعم تصدير XML إلى التسلسل الهرمي لفئات الأشكال الهندسية.

تصدير أنواع مختلفة من الكائنات إلى تنسيق XML عبر كائن زائر.

// تُعلن واجهة العنصر عن أسلوب `accept` يأخذ
// واجهة الزائر الأساسية كوسيطة.
interface Shape is
    method move(x, y)
    method draw()
    method accept(v: Visitor)

// يجب أن تُنفِّذ كل فئة عنصر ملموسة أسلوب `accept`
// بطريقة تستدعي أسلوب الزائر المقابل لفئة العنصر.
class Dot implements Shape is
    // ...

    // لاحظ أننا ندعو `visitDot`، الذي يطابق
    // اسم الفئة الحالية. بهذه الطريقة نُعلم الزائر بفئة
    // العنصر الذي يعمل معه.
    method accept(v: Visitor) is
        v.visitDot(this)

class Circle implements Shape is
    // ...
    method accept(v: Visitor) is
        v.visitCircle(this)

class Rectangle implements Shape is
    // ...
    method accept(v: Visitor) is
        v.visitRectangle(this)

class CompoundShape implements Shape is
    // ...
    method accept(v: Visitor) is
        v.visitCompoundShape(this)

// تُعلن واجهة الزائر عن مجموعة من أساليب الزيارة التي
// تتوافق مع فئات العنصر. يتيح توقيع أسلوب الزيارة
// للزائر تحديد الفئة الدقيقة للعنصر الذي يتعامل معه.
interface Visitor is
    method visitDot(d: Dot)
    method visitCircle(c: Circle)
    method visitRectangle(r: Rectangle)
    method visitCompoundShape(cs: CompoundShape)

// تُنفِّذ الزوار الملموسون عدة إصدارات من نفس
// الخوارزمية، التي يمكنها العمل مع جميع فئات العنصر الملموسة.
//
// يمكنك الاستفادة من أكبر ميزة لنمط الزائر
// عند استخدامه مع هيكل كائن معقد مثل
// شجرة Composite. في هذه الحالة، قد يكون من المفيد تخزين
// بعض الحالة الوسيطة للخوارزمية أثناء تنفيذ
// أساليب الزائر على كائنات مختلفة من الهيكل.
class XMLExportVisitor implements Visitor is
    method visitDot(d: Dot) is
        // تصدير معرّف النقطة وإحداثيات المركز.

    method visitCircle(c: Circle) is
        // تصدير معرّف الدائرة وإحداثيات المركز ونصف القطر.

    method visitRectangle(r: Rectangle) is
        // تصدير معرّف المستطيل وإحداثيات الزاوية العلوية اليسرى،
        // والعرض والارتفاع.

    method visitCompoundShape(cs: CompoundShape) is
        // تصدير معرّف الشكل بالإضافة إلى قائمة معرّفات عناصره الفرعية.

// يمكن لكود العميل تشغيل عمليات الزائر على أي مجموعة من
// العناصر دون معرفة فئاتها الملموسة. تُوجِّه عملية
// القبول الاستدعاء إلى العملية المناسبة
// في كائن الزائر.
class Application is
    field allShapes: array of Shapes

    method export() is
        exportVisitor = new XMLExportVisitor()

        foreach (shape in allShapes) do
            shape.accept(exportVisitor)

إذا تساءلت لماذا نحتاج أسلوب `accept` في هذا المثال، فإن مقالتي [الزائر والإرسال المزدوج](/ar/design-patterns/visitor-double-dispatch) تتناول هذا السؤال بالتفصيل.

## Applicability

استخدم الزائر عندما تحتاج إلى تنفيذ عملية على جميع عناصر هيكل كائن معقد (على سبيل المثال، شجرة كائنات).

 يتيح لك نمط الزائر تنفيذ عملية على مجموعة من الكائنات ذات فئات مختلفة، من خلال جعل كائن الزائر ينفّذ عدة متغيرات من نفس العملية تتوافق مع جميع الفئات المستهدفة.

 استخدم الزائر لتنظيف منطق الأعمال من السلوكيات المساعدة.

 يتيح لك النمط جعل الفئات الرئيسية في تطبيقك أكثر تركيزاً على مهامها الأساسية من خلال استخراج جميع السلوكيات الأخرى إلى مجموعة من فئات الزائر.

 استخدم النمط عندما يكون للسلوك معنى في بعض فئات التسلسل الهرمي للفئات فقط، دون غيرها.

 يمكنك استخراج هذا السلوك إلى فئة زائر منفصلة وتنفيذ أساليب الزيارة التي تقبل كائنات الفئات ذات الصلة فقط، مع ترك الباقي فارغاً.

## How to Implement

1. أعلن عن واجهة الزائر بمجموعة من أساليب "الزيارة"، أسلوب واحد لكل فئة عنصر ملموسة موجودة في البرنامج.
2. أعلن عن واجهة العنصر. إذا كنت تعمل مع تسلسل هرمي موجود لفئات العنصر، أضف أسلوب "القبول" المجرد إلى الفئة الأساسية للتسلسل الهرمي. يجب أن يقبل هذا الأسلوب كائن زائر كوسيطة.
3. نفِّذ أساليب القبول في جميع فئات العنصر الملموسة. يجب أن تُعيد هذه الأساليب توجيه الاستدعاء إلى أسلوب زيارة على كائن الزائر الوارد الذي يطابق فئة العنصر الحالي.
4. يجب أن تعمل فئات العنصر مع الزوار عبر واجهة الزائر فقط. يجب أن يكون الزوار، مع ذلك، على دراية بجميع فئات العنصر الملموسة، المُشار إليها كأنواع معاملات لأساليب الزيارة.
5. لكل سلوك لا يمكن تنفيذه داخل التسلسل الهرمي للعناصر، أنشئ فئة زائر ملموسة جديدة ونفِّذ جميع أساليب الزيارة.
قد تواجه موقفاً يحتاج فيه الزائر إلى الوصول إلى بعض الأعضاء الخاصة في فئة العنصر. في هذه الحالة، يمكنك إما جعل هذه الحقول أو الأساليب عامة، مما ينتهك تغليف العنصر، أو تضمين فئة الزائر في فئة العنصر. الخيار الأخير ممكن فقط إذا كنت محظوظاً بالعمل مع لغة برمجة تدعم الفئات المتداخلة.
6. يجب على العميل إنشاء كائنات الزائر وتمريرها إلى العناصر عبر أساليب "القبول".

## Pros

* _مبدأ المفتوح/المغلق_. يمكنك إدخال سلوك جديد يعمل مع كائنات من فئات مختلفة دون تغيير هذه الفئات.
* _مبدأ المسؤولية الواحدة_. يمكنك نقل إصدارات متعددة من نفس السلوك إلى نفس الفئة.
* يمكن لكائن الزائر تجميع بعض المعلومات المفيدة أثناء عمله مع كائنات مختلفة. قد يكون هذا مفيداً عندما تريد اجتياز هيكل كائن معقد، مثل شجرة كائنات، وتطبيق الزائر على كل كائن من هذا الهيكل.

## Cons

* تحتاج إلى تحديث جميع الزوار في كل مرة تُضاف فيها فئة إلى التسلسل الهرمي للعناصر أو تُزال منه.
* قد يفتقر الزوار إلى الصلاحيات اللازمة للوصول إلى الحقول والأساليب الخاصة بالعناصر التي من المفترض أن يعملوا معها.

## Relations with Other Patterns

* يمكنك التعامل مع [الزائر](/ar/design-patterns/visitor) باعتباره إصداراً قوياً من نمط [الأمر](/ar/design-patterns/command). يمكن لكائناته تنفيذ عمليات على كائنات مختلفة من فئات متنوعة.
* يمكنك استخدام [الزائر](/ar/design-patterns/visitor) لتنفيذ عملية على شجرة [المركّب](/ar/design-patterns/composite) بأكملها.
* يمكنك استخدام [الزائر](/ar/design-patterns/visitor) جنباً إلى جنب مع [المكرّر](/ar/design-patterns/iterator) لاجتياز هيكل بيانات معقد وتنفيذ عملية على عناصره، حتى لو كانت جميعها ذات فئات مختلفة.

## Extra

* هل تتساءل لماذا لا يمكننا ببساطة استبدال نمط الزائر بالتحميل الزائد للأساليب؟ اقرأ مقالتي [الزائر والإرسال المزدوج](/ar/design-patterns/visitor-double-dispatch) لتتعرّف على التفاصيل الدقيقة.
## Relations

**Related patterns**

- [الأمر (Command)](/ar/design-patterns/command.md)
- [المركَّب](/ar/design-patterns/composite.md)
- [المُكرِّر](/ar/design-patterns/iterator.md)

## Code Examples

### java

```java
package refactoring_guru.visitor.example.shapes;

import refactoring_guru.visitor.example.visitor.Visitor;

public interface Shape {
    void move(int x, int y);
    void draw();
    String accept(Visitor visitor);
}

package refactoring_guru.visitor.example.shapes;

import refactoring_guru.visitor.example.visitor.Visitor;

public class Dot implements Shape {
    private int id;
    private int x;
    private int y;

    public Dot() {
    }

    public Dot(int id, int x, int y) {
        this.id = id;
        this.x = x;
        this.y = y;
    }

    @Override
    public void move(int x, int y) {
        // تحريك الشكل
    }

    @Override
    public void draw() {
        // رسم الشكل
    }

    @Override
    public String accept(Visitor visitor) {
        return visitor.visitDot(this);
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int getId() {
        return id;
    }
}

package refactoring_guru.visitor.example.shapes;

import refactoring_guru.visitor.example.visitor.Visitor;

public class Circle extends Dot {
    private int radius;

    public Circle(int id, int x, int y, int radius) {
        super(id, x, y);
        this.radius = radius;
    }

    @Override
    public String accept(Visitor visitor) {
        return visitor.visitCircle(this);
    }

    public int getRadius() {
        return radius;
    }
}

package refactoring_guru.visitor.example.shapes;

import refactoring_guru.visitor.example.visitor.Visitor;

public class Rectangle implements Shape {
    private int id;
    private int x;
    private int y;
    private int width;
    private int height;

    public Rectangle(int id, int x, int y, int width, int height) {
        this.id = id;
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }

    @Override
    public String accept(Visitor visitor) {
        return visitor.visitRectangle(this);
    }

    @Override
    public void move(int x, int y) {
        // تحريك الشكل
    }

    @Override
    public void draw() {
        // رسم الشكل
    }

    public int getId() {
        return id;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }
}

package refactoring_guru.visitor.example.shapes;

import refactoring_guru.visitor.example.visitor.Visitor;

import java.util.ArrayList;
import java.util.List;

public class CompoundShape implements Shape {
    public int id;
    public List<Shape> children = new ArrayList<>();

    public CompoundShape(int id) {
        this.id = id;
    }

    @Override
    public void move(int x, int y) {
        // تحريك الشكل
    }

    @Override
    public void draw() {
        // رسم الشكل
    }

    public int getId() {
        return id;
    }

    @Override
    public String accept(Visitor visitor) {
        return visitor.visitCompoundGraphic(this);
    }

    public void add(Shape shape) {
        children.add(shape);
    }
}

package refactoring_guru.visitor.example.visitor;

import refactoring_guru.visitor.example.shapes.Circle;
import refactoring_guru.visitor.example.shapes.CompoundShape;
import refactoring_guru.visitor.example.shapes.Dot;
import refactoring_guru.visitor.example.shapes.Rectangle;

public interface Visitor {
    String visitDot(Dot dot);

    String visitCircle(Circle circle);

    String visitRectangle(Rectangle rectangle);

    String visitCompoundGraphic(CompoundShape cg);
}

package refactoring_guru.visitor.example.visitor;

import refactoring_guru.visitor.example.shapes.*;

public class XMLExportVisitor implements Visitor {

    public String export(Shape... args) {
        StringBuilder sb = new StringBuilder();
        sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "\n");
        for (Shape shape : args) {
            sb.append(shape.accept(this)).append("\n");
        }
        return sb.toString();
    }

    public String visitDot(Dot d) {
        return "<dot>" + "\n" +
                "    <id>" + d.getId() + "</id>" + "\n" +
                "    <x>" + d.getX() + "</x>" + "\n" +
                "    <y>" + d.getY() + "</y>" + "\n" +
                "</dot>";
    }

    public String visitCircle(Circle c) {
        return "<circle>" + "\n" +
                "    <id>" + c.getId() + "</id>" + "\n" +
                "    <x>" + c.getX() + "</x>" + "\n" +
                "    <y>" + c.getY() + "</y>" + "\n" +
                "    <radius>" + c.getRadius() + "</radius>" + "\n" +
                "</circle>";
    }

    public String visitRectangle(Rectangle r) {
        return "<rectangle>" + "\n" +
                "    <id>" + r.getId() + "</id>" + "\n" +
                "    <x>" + r.getX() + "</x>" + "\n" +
                "    <y>" + r.getY() + "</y>" + "\n" +
                "    <width>" + r.getWidth() + "</width>" + "\n" +
                "    <height>" + r.getHeight() + "</height>" + "\n" +
                "</rectangle>";
    }

    public String visitCompoundGraphic(CompoundShape cg) {
        return "<compound_graphic>" + "\n" +
                "   <id>" + cg.getId() + "</id>" + "\n" +
                _visitCompoundGraphic(cg) +
                "</compound_graphic>";
    }

    private String _visitCompoundGraphic(CompoundShape cg) {
        StringBuilder sb = new StringBuilder();
        for (Shape shape : cg.children) {
            String obj = shape.accept(this);
            // المسافة البادئة الصحيحة للكائنات الفرعية.
            obj = "    " + obj.replace("\n", "\n    ") + "\n";
            sb.append(obj);
        }
        return sb.toString();
    }

}

package refactoring_guru.visitor.example;

import refactoring_guru.visitor.example.shapes.*;
import refactoring_guru.visitor.example.visitor.XMLExportVisitor;

public class Demo {
    public static void main(String[] args) {
        Dot dot = new Dot(1, 10, 55);
        Circle circle = new Circle(2, 23, 15, 10);
        Rectangle rectangle = new Rectangle(3, 10, 17, 20, 30);

        CompoundShape compoundShape = new CompoundShape(4);
        compoundShape.add(dot);
        compoundShape.add(circle);
        compoundShape.add(rectangle);

        CompoundShape c = new CompoundShape(5);
        c.add(dot);
        compoundShape.add(c);

        export(circle, compoundShape);
    }

    private static void export(Shape... shapes) {
        XMLExportVisitor exportVisitor = new XMLExportVisitor();
        System.out.println(exportVisitor.export(shapes));
    }
}

<?xml version="1.0" encoding="utf-8"?>
<circle>
    <id>2</id>
    <x>23</x>
    <y>15</y>
    <radius>10</radius>
</circle>

<?xml version="1.0" encoding="utf-8"?>
<compound_graphic>
   <id>4</id>
    <dot>
        <id>1</id>
        <x>10</x>
        <y>55</y>
    </dot>
    <circle>
        <id>2</id>
        <x>23</x>
        <y>15</y>
        <radius>10</radius>
    </circle>
    <rectangle>
        <id>3</id>
        <x>10</x>
        <y>17</y>
        <width>20</width>
        <height>30</height>
    </rectangle>
    <compound_graphic>
       <id>5</id>
        <dot>
            <id>1</id>
            <x>10</x>
            <y>55</y>
        </dot>
    </compound_graphic>
</compound_graphic>
```

### csharp

```csharp
using System;
using System.Collections.Generic;

namespace RefactoringGuru.DesignPatterns.Visitor.Conceptual
{
    // تُعلن واجهة المكوّن عن أسلوب `accept` يأخذ
    // واجهة الزائر الأساسية كوسيطة.
    public interface IComponent
    {
        void Accept(IVisitor visitor);
    }

    // Each Concrete Component must implement the `Accept` method in such a way
    // that it calls the visitor's method corresponding to the component's
    // class.
    public class ConcreteComponentA : IComponent
    {
        // لاحظ أننا ندعو `VisitConcreteComponentA`، الذي يطابق
        // اسم الفئة الحالية. بهذه الطريقة نُعلم الزائر بفئة
        // المكوّن الذي يعمل معه.
        public void Accept(IVisitor visitor)
        {
            visitor.VisitConcreteComponentA(this);
        }

        // قد تحتوي المكوّنات الملموسة على أساليب خاصة غير موجودة في
        // فئتها الأساسية أو واجهتها. لا يزال الزائر قادراً على
        // استخدام هذه الأساليب لأنه يعلم بالفئة الملموسة للمكوّن.
        public string ExclusiveMethodOfConcreteComponentA()
        {
            return "A";
        }
    }

    public class ConcreteComponentB : IComponent
    {
        // نفس الأمر هنا: VisitConcreteComponentB => ConcreteComponentB
        public void Accept(IVisitor visitor)
        {
            visitor.VisitConcreteComponentB(this);
        }

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

    // تُعلن واجهة Visitor عن مجموعة من أساليب الزيارة المقابلة
    // لفئات المكوّن. يتيح توقيع أسلوب الزيارة للزائر
    // تحديد الفئة الدقيقة للمكوّن الذي يتعامل معه.
    public interface IVisitor
    {
        void VisitConcreteComponentA(ConcreteComponentA element);

        void VisitConcreteComponentB(ConcreteComponentB element);
    }

    // تُنفِّذ الزوار الملموسون عدة إصدارات من نفس الخوارزمية التي
    // يمكنها العمل مع جميع فئات المكوّن الملموسة.
    //
    // يمكنك الاستفادة من أكبر ميزة لنمط الزائر عند استخدامه
    // مع هيكل كائن معقد، مثل شجرة Composite. في هذه
    // الحالة، قد يكون من المفيد تخزين بعض الحالة الوسيطة
    // للخوارزمية أثناء تنفيذ أساليب الزائر على كائنات مختلفة
    // من الهيكل.
    class ConcreteVisitor1 : IVisitor
    {
        public void VisitConcreteComponentA(ConcreteComponentA element)
        {
            Console.WriteLine(element.ExclusiveMethodOfConcreteComponentA() + " + ConcreteVisitor1");
        }

        public void VisitConcreteComponentB(ConcreteComponentB element)
        {
            Console.WriteLine(element.SpecialMethodOfConcreteComponentB() + " + ConcreteVisitor1");
        }
    }

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

        public void VisitConcreteComponentB(ConcreteComponentB element)
        {
            Console.WriteLine(element.SpecialMethodOfConcreteComponentB() + " + ConcreteVisitor2");
        }
    }

    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()
            };

            Console.WriteLine("The client code works with all visitors via the base Visitor interface:");
            var visitor1 = new ConcreteVisitor1();
            Client.ClientCode(components,visitor1);

            Console.WriteLine();

            Console.WriteLine("It allows the same client code to work with different types of visitors:");
            var visitor2 = new ConcreteVisitor2();
            Client.ClientCode(components, visitor2);
        }
    }
}

The client code works with all visitors via the base Visitor interface:
A + ConcreteVisitor1
B + ConcreteVisitor1

It allows the same client code to work with different types of visitors:
A + ConcreteVisitor2
B + ConcreteVisitor2
```

### cpp

```cpp
/**
 * تُعلن واجهة الزائر عن مجموعة من أساليب الزيارة المقابلة لفئات المكوّن.
 * يتيح توقيع أسلوب الزيارة للزائر تحديد الفئة الدقيقة
 * للمكوّن الذي يتعامل معه.
 */
class ConcreteComponentA;
class ConcreteComponentB;

class Visitor {
 public:
  virtual void VisitConcreteComponentA(const ConcreteComponentA *element) const = 0;
  virtual void VisitConcreteComponentB(const ConcreteComponentB *element) const = 0;
};

/**
 * تُعلن واجهة المكوّن عن أسلوب `accept` يجب أن يأخذ
 * واجهة الزائر الأساسية كوسيطة.
 */

class Component {
 public:
  virtual ~Component() {}
  virtual void Accept(Visitor *visitor) const = 0;
};

/**
 * Each Concrete Component must implement the `Accept` method in such a way that
 * it calls the visitor's method corresponding to the component's class.
 */
class ConcreteComponentA : public Component {
  /**
   * لاحظ أننا ندعو `visitConcreteComponentA`، الذي يطابق
   * اسم الفئة الحالية. بهذه الطريقة نُعلم الزائر بفئة
   * المكوّن الذي يعمل معه.
   */
 public:
  void Accept(Visitor *visitor) const override {
    visitor->VisitConcreteComponentA(this);
  }
  /**
   * قد تحتوي المكوّنات الملموسة على أساليب خاصة غير موجودة في فئتها
   * الأساسية أو واجهتها. لا يزال الزائر قادراً على استخدام هذه الأساليب
   * لأنه على دراية بالفئة الملموسة للمكوّن.
   */
  std::string ExclusiveMethodOfConcreteComponentA() const {
    return "A";
  }
};

class ConcreteComponentB : public Component {
  /**
   * نفس الأمر هنا: visitConcreteComponentB => ConcreteComponentB
   */
 public:
  void Accept(Visitor *visitor) const override {
    visitor->VisitConcreteComponentB(this);
  }
  std::string SpecialMethodOfConcreteComponentB() const {
    return "B";
  }
};

/**
 * تُنفِّذ الزوار الملموسون عدة إصدارات من نفس الخوارزمية التي يمكنها
 * العمل مع جميع فئات المكوّن الملموسة.
 *
 * يمكنك الاستفادة من أكبر ميزة لنمط الزائر عند استخدامه مع هيكل كائن معقد،
 * مثل شجرة Composite. في هذه الحالة، قد يكون من المفيد تخزين بعض
 * الحالة الوسيطة للخوارزمية أثناء تنفيذ أساليب الزائر على كائنات مختلفة
 * من الهيكل.
 */
class ConcreteVisitor1 : public Visitor {
 public:
  void VisitConcreteComponentA(const ConcreteComponentA *element) const override {
    std::cout << element->ExclusiveMethodOfConcreteComponentA() << " + ConcreteVisitor1\n";
  }

  void VisitConcreteComponentB(const ConcreteComponentB *element) const override {
    std::cout << element->SpecialMethodOfConcreteComponentB() << " + ConcreteVisitor1\n";
  }
};

class ConcreteVisitor2 : public Visitor {
 public:
  void VisitConcreteComponentA(const ConcreteComponentA *element) const override {
    std::cout << element->ExclusiveMethodOfConcreteComponentA() << " + ConcreteVisitor2\n";
  }
  void VisitConcreteComponentB(const ConcreteComponentB *element) const override {
    std::cout << element->SpecialMethodOfConcreteComponentB() << " + ConcreteVisitor2\n";
  }
};
/**
 * يمكن لكود العميل تشغيل عمليات الزائر على أي مجموعة من العناصر دون
 * معرفة فئاتها الملموسة. تُوجِّه عملية القبول الاستدعاء إلى العملية
 * المناسبة في كائن الزائر.
 */
void ClientCode(std::array<const Component *, 2> components, Visitor *visitor) {
  // ...
  for (const Component *comp : components) {
    comp->Accept(visitor);
  }
  // ...
}

int main() {
  std::array<const Component *, 2> components = {new ConcreteComponentA, new ConcreteComponentB};
  std::cout << "The client code works with all visitors via the base Visitor interface:\n";
  ConcreteVisitor1 *visitor1 = new ConcreteVisitor1;
  ClientCode(components, visitor1);
  std::cout << "\n";
  std::cout << "It allows the same client code to work with different types of visitors:\n";
  ConcreteVisitor2 *visitor2 = new ConcreteVisitor2;
  ClientCode(components, visitor2);

  for (const Component *comp : components) {
    delete comp;
  }
  delete visitor1;
  delete visitor2;

  return 0;
}

The client code works with all visitors via the base Visitor interface:
A + ConcreteVisitor1
B + ConcreteVisitor1

It allows the same client code to work with different types of visitors:
A + ConcreteVisitor2
B + ConcreteVisitor2
```

### go

```go
type visitor interface {
   visitForSquare(square)
   visitForCircle(circle)
   visitForTriangle(triangle)
}

func accept(v visitor)

func (obj *square) accept(v visitor){
    v.visitForSquare(obj)
}

package main

type Shape interface {
	getType() string
	accept(Visitor)
}

package main

type Square struct {
	side int
}

func (s *Square) accept(v Visitor) {
	v.visitForSquare(s)
}

func (s *Square) getType() string {
	return "Square"
}

package main

type Circle struct {
	radius int
}

func (c *Circle) accept(v Visitor) {
	v.visitForCircle(c)
}

func (c *Circle) getType() string {
	return "Circle"
}

package main

type Rectangle struct {
	l int
	b int
}

func (t *Rectangle) accept(v Visitor) {
	v.visitForrectangle(t)
}

func (t *Rectangle) getType() string {
	return "rectangle"
}

package main

type Visitor interface {
	visitForSquare(*Square)
	visitForCircle(*Circle)
	visitForrectangle(*Rectangle)
}

package main

import (
	"fmt"
)

type AreaCalculator struct {
	area int
}

func (a *AreaCalculator) visitForSquare(s *Square) {
	// حساب مساحة المربع.
	// ثم تعيينها في متغير مثيل المساحة.
	fmt.Println("Calculating area for square")
}

func (a *AreaCalculator) visitForCircle(s *Circle) {
	fmt.Println("Calculating area for circle")
}
func (a *AreaCalculator) visitForrectangle(s *Rectangle) {
	fmt.Println("Calculating area for rectangle")
}

package main

import "fmt"

type MiddleCoordinates struct {
	x int
	y int
}

func (a *MiddleCoordinates) visitForSquare(s *Square) {
	// حساب إحداثيات النقطة الوسطى للمربع.
	// ثم تعيينها في متغيرَي مثيل x و y.
	fmt.Println("Calculating middle point coordinates for square")
}

func (a *MiddleCoordinates) visitForCircle(c *Circle) {
	fmt.Println("Calculating middle point coordinates for circle")
}
func (a *MiddleCoordinates) visitForrectangle(t *Rectangle) {
	fmt.Println("Calculating middle point coordinates for rectangle")
}

package main

import "fmt"

func main() {
	square := &Square{side: 2}
	circle := &Circle{radius: 3}
	rectangle := &Rectangle{l: 2, b: 3}

	areaCalculator := &AreaCalculator{}

	square.accept(areaCalculator)
	circle.accept(areaCalculator)
	rectangle.accept(areaCalculator)

	fmt.Println()
	middleCoordinates := &MiddleCoordinates{}
	square.accept(middleCoordinates)
	circle.accept(middleCoordinates)
	rectangle.accept(middleCoordinates)
}

Calculating area for square
Calculating area for circle
Calculating area for rectangle

Calculating middle point coordinates for square
Calculating middle point coordinates for circle
Calculating middle point coordinates for rectangle
```

### php

```php
<?php

namespace RefactoringGuru\Visitor\Conceptual;

/**
 * تُعلن واجهة المكوّن عن أسلوب `accept` يجب أن يأخذ
 * واجهة الزائر الأساسية كوسيطة.
 */
interface Component
{
    public function accept(Visitor $visitor): void;
}

/**
 * يجب أن يُنفِّذ كل مكوّن ملموس أسلوب `accept` بطريقة
 * تستدعي أسلوب الزائر المقابل لفئة المكوّن.
 */
class ConcreteComponentA implements Component
{
    /**
     * لاحظ أننا ندعو `visitConcreteComponentA`، الذي يطابق
     * اسم الفئة الحالية. بهذه الطريقة نُعلم الزائر بفئة
     * المكوّن الذي يعمل معه.
     */
    public function accept(Visitor $visitor): void
    {
        $visitor->visitConcreteComponentA($this);
    }

    /**
     * قد تحتوي المكوّنات الملموسة على أساليب خاصة غير موجودة في
     * فئتها الأساسية أو واجهتها. لا يزال الزائر قادراً على استخدام
     * هذه الأساليب لأنه يعلم بالفئة الملموسة للمكوّن.
     */
    public function exclusiveMethodOfConcreteComponentA(): string
    {
        return "A";
    }
}

class ConcreteComponentB implements Component
{
    /**
     * نفس الأمر هنا: visitConcreteComponentB => ConcreteComponentB
     */
    public function accept(Visitor $visitor): void
    {
        $visitor->visitConcreteComponentB($this);
    }

    public function specialMethodOfConcreteComponentB(): string
    {
        return "B";
    }
}

/**
 * تُعلن واجهة الزائر عن مجموعة من أساليب الزيارة المقابلة لفئات المكوّن.
 * يتيح توقيع أسلوب الزيارة للزائر تحديد الفئة الدقيقة
 * للمكوّن الذي يتعامل معه.
 */
interface Visitor
{
    public function visitConcreteComponentA(ConcreteComponentA $element): void;

    public function visitConcreteComponentB(ConcreteComponentB $element): void;
}

/**
 * تُنفِّذ الزوار الملموسون عدة إصدارات من نفس الخوارزمية التي يمكنها
 * العمل مع جميع فئات المكوّن الملموسة.
 *
 * يمكنك الاستفادة من أكبر ميزة لنمط الزائر عند استخدامه مع هيكل كائن معقد،
 * مثل شجرة Composite. في هذه الحالة، قد يكون من المفيد تخزين بعض
 * الحالة الوسيطة للخوارزمية أثناء تنفيذ أساليب الزائر على كائنات مختلفة
 * من الهيكل.
 */
class ConcreteVisitor1 implements Visitor
{
    public function visitConcreteComponentA(ConcreteComponentA $element): void
    {
        echo $element->exclusiveMethodOfConcreteComponentA() . " + ConcreteVisitor1\n";
    }

    public function visitConcreteComponentB(ConcreteComponentB $element): void
    {
        echo $element->specialMethodOfConcreteComponentB() . " + ConcreteVisitor1\n";
    }
}

class ConcreteVisitor2 implements Visitor
{
    public function visitConcreteComponentA(ConcreteComponentA $element): void
    {
        echo $element->exclusiveMethodOfConcreteComponentA() . " + ConcreteVisitor2\n";
    }

    public function visitConcreteComponentB(ConcreteComponentB $element): void
    {
        echo $element->specialMethodOfConcreteComponentB() . " + ConcreteVisitor2\n";
    }
}

/**
 * يمكن لكود العميل تشغيل عمليات الزائر على أي مجموعة من العناصر دون
 * معرفة فئاتها الملموسة. تُوجِّه عملية القبول الاستدعاء إلى
 * العملية المناسبة في كائن الزائر.
 */
function clientCode(array $components, Visitor $visitor)
{
    // ...
    foreach ($components as $component) {
        $component->accept($visitor);
    }
    // ...
}

$components = [
    new ConcreteComponentA(),
    new ConcreteComponentB(),
];

echo "The client code works with all visitors via the base Visitor interface:\n";
$visitor1 = new ConcreteVisitor1();
clientCode($components, $visitor1);
echo "\n";

echo "It allows the same client code to work with different types of visitors:\n";
$visitor2 = new ConcreteVisitor2();
clientCode($components, $visitor2);

The client code works with all visitors via the base Visitor interface:
A + ConcreteVisitor1
B + ConcreteVisitor1

It allows the same client code to work with different types of visitors:
A + ConcreteVisitor2
B + ConcreteVisitor2

<?php

namespace RefactoringGuru\Visitor\RealWorld;

/**
 * تُعلن واجهة المكوّن عن أسلوب لقبول كائنات الزائر.
 *
 * في هذا الأسلوب، يجب على المكوّن الملموس استدعاء أسلوب محدد في الزائر
 * يمتلك نفس نوع المعامل الخاص بذلك المكوّن.
 */
interface Entity
{
    public function accept(Visitor $visitor): string;
}

/**
 * المكوّن الملموس للشركة.
 */
class Company implements Entity
{
    private $name;

    /**
     * @var Department[]
     */
    private $departments;

    public function __construct(string $name, array $departments)
    {
        $this->name = $name;
        $this->departments = $departments;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function getDepartments(): array
    {
        return $this->departments;
    }

    // ...

    public function accept(Visitor $visitor): string
    {
        // لاحظ أن مكوّن الشركة يجب أن يستدعي أسلوب visitCompany.
        // ينطبق نفس المبدأ على جميع المكوّنات.
        return $visitor->visitCompany($this);
    }
}

/**
 * المكوّن الملموس للقسم.
 */
class Department implements Entity
{
    private $name;

    /**
     * @var Employee[]
     */
    private $employees;

    public function __construct(string $name, array $employees)
    {
        $this->name = $name;
        $this->employees = $employees;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function getEmployees(): array
    {
        return $this->employees;
    }

    public function getCost(): int
    {
        $cost = 0;
        foreach ($this->employees as $employee) {
            $cost += $employee->getSalary();
        }

        return $cost;
    }

    // ...

    public function accept(Visitor $visitor): string
    {
        return $visitor->visitDepartment($this);
    }
}

/**
 * المكوّن الملموس للموظف.
 */
class Employee implements Entity
{
    private $name;

    private $position;

    private $salary;

    public function __construct(string $name, string $position, int $salary)
    {
        $this->name = $name;
        $this->position = $position;
        $this->salary = $salary;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function getPosition(): string
    {
        return $this->position;
    }

    public function getSalary(): int
    {
        return $this->salary;
    }

    // ...

    public function accept(Visitor $visitor): string
    {
        return $visitor->visitEmployee($this);
    }
}

/**
 * تُعلن واجهة الزائر عن مجموعة من أساليب الزيارة لكل
 * فئة من فئات المكوّن الملموسة.
 */
interface Visitor
{
    public function visitCompany(Company $company): string;

    public function visitDepartment(Department $department): string;

    public function visitEmployee(Employee $employee): string;
}

/**
 * يجب على الزائر الملموس توفير تطبيقات لكل فئة
 * من فئات المكوّنات الملموسة.
 */
class SalaryReport implements Visitor
{
    public function visitCompany(Company $company): string
    {
        $output = "";
        $total = 0;

        foreach ($company->getDepartments() as $department) {
            $total += $department->getCost();
            $output .= "\n--" . $this->visitDepartment($department);
        }

        $output = $company->getName() .
            " (" . money_format("%i", $total) . ")\n" . $output;

        return $output;
    }

    public function visitDepartment(Department $department): string
    {
        $output = "";

        foreach ($department->getEmployees() as $employee) {
            $output .= "   " . $this->visitEmployee($employee);
        }

        $output = $department->getName() .
            " (" . money_format("%i", $department->getCost()) . ")\n\n" .
            $output;

        return $output;
    }

    public function visitEmployee(Employee $employee): string
    {
        return money_format("%#6n", $employee->getSalary()) .
            " " . $employee->getName() .
            " (" . $employee->getPosition() . ")\n";
    }
}

/**
 * كود العميل.
 */

$mobileDev = new Department("Mobile Development", [
    new Employee("Albert Falmore", "designer", 100000),
    new Employee("Ali Halabay", "programmer", 100000),
    new Employee("Sarah Konor", "programmer", 90000),
    new Employee("Monica Ronaldino", "QA engineer", 31000),
    new Employee("James Smith", "QA engineer", 30000),
]);
$techSupport = new Department("Tech Support", [
    new Employee("Larry Ulbrecht", "supervisor", 70000),
    new Employee("Elton Pale", "operator", 30000),
    new Employee("Rajeet Kumar", "operator", 30000),
    new Employee("John Burnovsky", "operator", 34000),
    new Employee("Sergey Korolev", "operator", 35000),
]);
$company = new Company("SuperStarDevelopment", [$mobileDev, $techSupport]);

setlocale(LC_MONETARY, 'en_US');
$report = new SalaryReport();

echo "Client: I can print a report for a whole company:\n\n";
echo $company->accept($report);

echo "\nClient: ...or for different entities " .
    "such as an employee, a department, or the whole company:\n\n";
$someEmployee = new Employee("Some employee", "operator", 35000);
$differentEntities = [$someEmployee, $techSupport, $company];
foreach ($differentEntities as $entity) {
    echo $entity->accept($report) . "\r\n";
}

// $export = new JSONExport(); 
// echo $company->accept($export);

Client: I can print a report for a whole company:

SuperStarDevelopment (USD550,000.00)

--Mobile Development (USD351,000.00)

    $100,000.00 Albert Falmore (designer)
    $100,000.00 Ali Halabay (programmer)
    $ 90,000.00 Sarah Konor (programmer)
    $ 31,000.00 Monica Ronaldino (QA engineer)
    $ 30,000.00 James Smith (QA engineer)

--Tech Support (USD199,000.00)

    $ 70,000.00 Larry Ulbrecht (supervisor)
    $ 30,000.00 Elton Pale (operator)
    $ 30,000.00 Rajeet Kumar (operator)
    $ 34,000.00 John Burnovsky (operator)
    $ 35,000.00 Sergey Korolev (operator)


Client: ...or for different entities such as an employee, a department, or the whole company:

35000 Some employee (operator)

Tech Support (199000)

   70000 Larry Ulbrecht (supervisor)
   30000 Elton Pale (operator)
   30000 Rajeet Kumar (operator)
   34000 John Burnovsky (operator)
   35000 Sergey Korolev (operator)

SuperStarDevelopment (550000)

--Mobile Development (351000)

   100000 Albert Falmore (designer)
   100000 Ali Halabay (programmer)
   90000 Sarah Konor (programmer)
   31000 Monica Ronaldino (QA engineer)
   30000 James Smith (QA engineer)

--Tech Support (199000)

   70000 Larry Ulbrecht (supervisor)
   30000 Elton Pale (operator)
   30000 Rajeet Kumar (operator)
   34000 John Burnovsky (operator)
   35000 Sergey Korolev (operator)
```

### python

```python
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import List


class Component(ABC):
    """
    تُعلن واجهة المكوّن عن أسلوب `accept` يجب أن يأخذ
    واجهة الزائر الأساسية كوسيطة.
    """

    @abstractmethod
    def accept(self, visitor: Visitor) -> None:
        pass


class ConcreteComponentA(Component):
    """
    يجب أن تُنفِّذ كل مكوّن ملموس أسلوب `accept` بطريقة
    تستدعي أسلوب الزائر المقابل لفئة المكوّن.
    """

    def accept(self, visitor: Visitor) -> None:
        """
        لاحظ أننا ندعو `visitConcreteComponentA`، الذي يطابق
        اسم الفئة الحالية. بهذه الطريقة نُعلم الزائر بفئة
        المكوّن الذي يعمل معه.
        """

        visitor.visit_concrete_component_a(self)

    def exclusive_method_of_concrete_component_a(self) -> str:
        """
        قد تحتوي المكوّنات الملموسة على أساليب خاصة غير موجودة في
        فئتها الأساسية أو واجهتها. لا يزال الزائر قادراً على استخدام
        هذه الأساليب لأنه على دراية بالفئة الملموسة للمكوّن.
        """

        return "A"


class ConcreteComponentB(Component):
    """
    نفس الأمر هنا: visitConcreteComponentB => ConcreteComponentB
    """

    def accept(self, visitor: Visitor):
        visitor.visit_concrete_component_b(self)

    def special_method_of_concrete_component_b(self) -> str:
        return "B"


class Visitor(ABC):
    """
    تُعلن واجهة الزائر عن مجموعة من أساليب الزيارة المقابلة لفئات المكوّن.
    يتيح توقيع أسلوب الزيارة للزائر تحديد الفئة الدقيقة
    للمكوّن الذي يتعامل معه.
    """

    @abstractmethod
    def visit_concrete_component_a(self, element: ConcreteComponentA) -> None:
        pass

    @abstractmethod
    def visit_concrete_component_b(self, element: ConcreteComponentB) -> None:
        pass


"""
تُنفِّذ الزوار الملموسون عدة إصدارات من نفس الخوارزمية التي يمكنها العمل مع
جميع فئات المكوّن الملموسة.

يمكنك الاستفادة من أكبر ميزة لنمط الزائر عند استخدامه مع هيكل كائن معقد،
مثل شجرة Composite. في هذه الحالة، قد يكون من المفيد تخزين بعض
الحالة الوسيطة للخوارزمية أثناء تنفيذ أساليب الزائر على كائنات مختلفة
من الهيكل.
"""


class ConcreteVisitor1(Visitor):
    def visit_concrete_component_a(self, element) -> None:
        print(f"{element.exclusive_method_of_concrete_component_a()} + ConcreteVisitor1")

    def visit_concrete_component_b(self, element) -> None:
        print(f"{element.special_method_of_concrete_component_b()} + ConcreteVisitor1")


class ConcreteVisitor2(Visitor):
    def visit_concrete_component_a(self, element) -> None:
        print(f"{element.exclusive_method_of_concrete_component_a()} + ConcreteVisitor2")

    def visit_concrete_component_b(self, element) -> None:
        print(f"{element.special_method_of_concrete_component_b()} + ConcreteVisitor2")


def client_code(components: List[Component], visitor: Visitor) -> None:
    """
    يمكن لكود العميل تشغيل عمليات الزائر على أي مجموعة من العناصر دون
    معرفة فئاتها الملموسة. تُوجِّه عملية القبول الاستدعاء إلى
    العملية المناسبة في كائن الزائر.
    """

    # ...
    for component in components:
        component.accept(visitor)
    # ...


if __name__ == "__main__":
    components = [ConcreteComponentA(), ConcreteComponentB()]

    print("The client code works with all visitors via the base Visitor interface:")
    visitor1 = ConcreteVisitor1()
    client_code(components, visitor1)

    print("It allows the same client code to work with different types of visitors:")
    visitor2 = ConcreteVisitor2()
    client_code(components, visitor2)

The client code works with all visitors via the base Visitor interface:
A + ConcreteVisitor1
B + ConcreteVisitor1
It allows the same client code to work with different types of visitors:
A + ConcreteVisitor2
B + ConcreteVisitor2
```

### ruby

```ruby
# تُعلن واجهة المكوّن عن أسلوب `accept` يجب أن يأخذ
# واجهة الزائر الأساسية كوسيطة.
class Component
  # @abstract
  #
  # @param [Visitor] visitor
  def accept(_visitor)
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end

# يجب أن تُنفِّذ كل مكوّن ملموس أسلوب `accept` بطريقة
# تستدعي أسلوب الزائر المقابل لفئة المكوّن.
class ConcreteComponentA < Component
  # لاحظ أننا ندعو `visitConcreteComponentA`، الذي يطابق
  # اسم الفئة الحالية. بهذه الطريقة نُعلم الزائر بفئة
  # المكوّن الذي يعمل معه.
  def accept(visitor)
    visitor.visit_concrete_component_a(self)
  end

  # قد تحتوي المكوّنات الملموسة على أساليب خاصة غير موجودة في فئتها
  # الأساسية أو واجهتها. لا يزال الزائر قادراً على استخدام هذه الأساليب
  # لأنه على دراية بالفئة الملموسة للمكوّن.
  def exclusive_method_of_concrete_component_a
    'A'
  end
end

# نفس الأمر هنا: visit_concrete_component_b => ConcreteComponentB
class ConcreteComponentB < Component
  # @param [Visitor] visitor
  def accept(visitor)
    visitor.visit_concrete_component_b(self)
  end

  def special_method_of_concrete_component_b
    'B'
  end
end

# تُعلن واجهة الزائر عن مجموعة من أساليب الزيارة المقابلة لفئات المكوّن.
# يتيح توقيع أسلوب الزيارة للزائر تحديد الفئة الدقيقة
# للمكوّن الذي يتعامل معه.
class Visitor
  # @abstract
  #
  # @param [ConcreteComponentA] element
  def visit_concrete_component_a(_element)
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end

  # @abstract
  #
  # @param [ConcreteComponentB] element
  def visit_concrete_component_b(_element)
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end

# تُنفِّذ الزوار الملموسون عدة إصدارات من نفس الخوارزمية التي يمكنها
# العمل مع جميع فئات المكوّن الملموسة.
#
# يمكنك الاستفادة من أكبر ميزة لنمط الزائر عند استخدامه مع هيكل
# كائن معقد، مثل شجرة Composite. في هذه الحالة، قد يكون من المفيد
# تخزين بعض الحالة الوسيطة للخوارزمية أثناء تنفيذ أساليب الزائر
# على كائنات مختلفة من الهيكل.
class ConcreteVisitor1 < Visitor
  def visit_concrete_component_a(element)
    puts "#{element.exclusive_method_of_concrete_component_a} + #{self.class}"
  end

  def visit_concrete_component_b(element)
    puts "#{element.special_method_of_concrete_component_b} + #{self.class}"
  end
end

class ConcreteVisitor2 < Visitor
  def visit_concrete_component_a(element)
    puts "#{element.exclusive_method_of_concrete_component_a} + #{self.class}"
  end

  def visit_concrete_component_b(element)
    puts "#{element.special_method_of_concrete_component_b} + #{self.class}"
  end
end

# يمكن لكود العميل تشغيل عمليات الزائر على أي مجموعة من العناصر دون
# معرفة فئاتها الملموسة. تُوجِّه عملية القبول الاستدعاء إلى
# العملية المناسبة في كائن الزائر.
def client_code(components, visitor)
  # ...
  components.each do |component|
    component.accept(visitor)
  end
  # ...
end

components = [ConcreteComponentA.new, ConcreteComponentB.new]

puts 'The client code works with all visitors via the base Visitor interface:'
visitor1 = ConcreteVisitor1.new
client_code(components, visitor1)

puts 'It allows the same client code to work with different types of visitors:'
visitor2 = ConcreteVisitor2.new
client_code(components, visitor2)

The client code works with all visitors via the base Visitor interface:
A + ConcreteVisitor1
B + ConcreteVisitor1
It allows the same client code to work with different types of visitors:
A + ConcreteVisitor2
B + ConcreteVisitor2
```

### rust

```rust
use crate::{TwoValuesArray, TwoValuesStruct};

/// يمكن للزائر زيارة نوع واحد، وإجراء تحويلات، وإخراج نوع آخر.
///
/// لا يعني ذلك أن جميع الزوار يجب أن يُعيدوا نوعاً جديداً، إنه مجرد مثال
/// يوضّح الأسلوب.
pub trait Visitor {
    type Value;

    /// Visits a vector of integers and outputs a desired type.
    fn visit_vec(&self, v: Vec<i32>) -> Self::Value;
}

/// تنفيذ الزائر لهيكل من قيمتين.
impl Visitor for TwoValuesStruct {
    type Value = TwoValuesStruct;

    fn visit_vec(&self, v: Vec<i32>) -> Self::Value {
        TwoValuesStruct { a: v[0], b: v[1] }
    }
}

/// تنفيذ الزائر لهيكل مصفوفة القيم.
impl Visitor for TwoValuesArray {
    type Value = TwoValuesArray;

    fn visit_vec(&self, v: Vec<i32>) -> Self::Value {
        let mut ab = [0i32; 2];

        ab[0] = v[0];
        ab[1] = v[1];

        TwoValuesArray { ab }
    }
}

#![allow(unused)]

mod visitor;

use visitor::Visitor;

/// هيكل من قيمتين صحيحتين.
///
/// سيكون مخرجاً لسمة `Visitor` المُعرَّفة للنوع
/// في `visitor.rs`.
#[derive(Default, Debug)]
pub struct TwoValuesStruct {
    a: i32,
    b: i32,
}

/// هيكل مصفوفة قيم.
///
/// سيكون مخرجاً لسمة `Visitor` المُعرَّفة للنوع
/// في `visitor.rs`.
#[derive(Default, Debug)]
pub struct TwoValuesArray {
    ab: [i32; 2],
}

/// تُعرِّف سمة `Deserializer` أساليب يمكنها تحليل سلسلة نصية أو
/// متجه، وتقبل زائراً يعلم كيفية إنشاء كائن جديد
/// من النوع المطلوب (في حالتنا، `TwoValuesArray` و `TwoValuesStruct`).
trait Deserializer<V: Visitor> {
    fn create(visitor: V) -> Self;
    fn parse_str(&self, input: &str) -> Result<V::Value, &'static str> {
        Err("parse_str is unimplemented")
    }
    fn parse_vec(&self, input: Vec<i32>) -> Result<V::Value, &'static str> {
        Err("parse_vec is unimplemented")
    }
}

struct StringDeserializer<V: Visitor> {
    visitor: V,
}

impl<V: Visitor> Deserializer<V> for StringDeserializer<V> {
    fn create(visitor: V) -> Self {
        Self { visitor }
    }

    fn parse_str(&self, input: &str) -> Result<V::Value, &'static str> {
        // في هذه الحالة، لتطبيق زائر، يجب على المُفكِّك القيام
        // ببعض التحضيرات. يقوم الزائر بدوره، لكنه لا يقوم بكل شيء.
        let input_vec = input
            .split_ascii_whitespace()
            .map(|x| x.parse().unwrap())
            .collect();

        Ok(self.visitor.visit_vec(input_vec))
    }
}

struct VecDeserializer<V: Visitor> {
    visitor: V,
}

impl<V: Visitor> Deserializer<V> for VecDeserializer<V> {
    fn create(visitor: V) -> Self {
        Self { visitor }
    }

    fn parse_vec(&self, input: Vec<i32>) -> Result<V::Value, &'static str> {
        Ok(self.visitor.visit_vec(input))
    }
}

fn main() {
    let deserializer = StringDeserializer::create(TwoValuesStruct::default());
    let result = deserializer.parse_str("123 456");
    println!("{:?}", result);

    let deserializer = VecDeserializer::create(TwoValuesStruct::default());
    let result = deserializer.parse_vec(vec![123, 456]);
    println!("{:?}", result);

    let deserializer = VecDeserializer::create(TwoValuesArray::default());
    let result = deserializer.parse_vec(vec![123, 456]);
    println!("{:?}", result);

    println!(
        "Error: {}",
        deserializer.parse_str("123 456").err().unwrap()
    )
}

Ok(TwoValuesStruct { a: 123, b: 456 })
Ok(TwoValuesStruct { a: 123, b: 456 })
Ok(TwoValuesArray { ab: [123, 456] })
Error: parse_str unimplemented
```

### swift

```swift
import XCTest

/// تُعلن واجهة المكوّن عن أسلوب `accept` يجب أن يأخذ
/// واجهة الزائر الأساسية كوسيطة.
protocol Component {

    func accept(_ visitor: Visitor)
}

/// Each Concrete Component must implement the `accept` method in such a way
/// that it calls the visitor's method corresponding to the component's class.
class ConcreteComponentA: Component {

    /// لاحظ أننا ندعو `visitConcreteComponentA`، الذي يطابق
    /// اسم الفئة الحالية. بهذه الطريقة نُعلم الزائر بفئة
    /// المكوّن الذي يعمل معه.
    func accept(_ visitor: Visitor) {
        visitor.visitConcreteComponentA(element: self)
    }

    /// قد تحتوي المكوّنات الملموسة على أساليب خاصة غير موجودة في
    /// فئتها الأساسية أو واجهتها. لا يزال الزائر قادراً على استخدام
    /// هذه الأساليب لأنه على دراية بالفئة الملموسة للمكوّن.
    func exclusiveMethodOfConcreteComponentA() -> String {
        return "A"
    }
}

class ConcreteComponentB: Component {

    /// نفس الأمر هنا: visitConcreteComponentB => ConcreteComponentB
    func accept(_ visitor: Visitor) {
        visitor.visitConcreteComponentB(element: self)
    }

    func specialMethodOfConcreteComponentB() -> String {
        return "B"
    }
}

/// تُعلن واجهة الزائر عن مجموعة من أساليب الزيارة المقابلة لفئات المكوّن.
/// يتيح توقيع أسلوب الزيارة للزائر تحديد الفئة الدقيقة
/// للمكوّن الذي يتعامل معه.
protocol Visitor {

    func visitConcreteComponentA(element: ConcreteComponentA)
    func visitConcreteComponentB(element: ConcreteComponentB)
}

/// تُنفِّذ الزوار الملموسون عدة إصدارات من نفس الخوارزمية التي
/// يمكنها العمل مع جميع فئات المكوّن الملموسة.
///
/// يمكنك الاستفادة من أكبر ميزة لنمط الزائر عند استخدامه مع هيكل
/// كائن معقد، مثل شجرة Composite. في هذه الحالة، قد يكون من المفيد
/// تخزين بعض الحالة الوسيطة للخوارزمية أثناء تنفيذ أساليب الزائر
/// على كائنات مختلفة من الهيكل.
class ConcreteVisitor1: Visitor {

    func visitConcreteComponentA(element: ConcreteComponentA) {
        print(element.exclusiveMethodOfConcreteComponentA() + " + ConcreteVisitor1\n")
    }

    func visitConcreteComponentB(element: ConcreteComponentB) {
        print(element.specialMethodOfConcreteComponentB() + " + ConcreteVisitor1\n")
    }
}

class ConcreteVisitor2: Visitor {

    func visitConcreteComponentA(element: ConcreteComponentA) {
        print(element.exclusiveMethodOfConcreteComponentA() + " + ConcreteVisitor2\n")
    }

    func visitConcreteComponentB(element: ConcreteComponentB) {
        print(element.specialMethodOfConcreteComponentB() + " + ConcreteVisitor2\n")
    }
}

/// The client code can run visitor operations over any set of elements without
/// figuring out their concrete classes. The accept operation directs a call to
/// the appropriate operation in the visitor object.
class Client {
    // ...
    static func clientCode(components: [Component], visitor: Visitor) {
        // ...
        components.forEach({ $0.accept(visitor) })
        // ...
    }
    // ...
}

/// لنرَ كيف يعمل كل شيء معاً.
class VisitorConceptual: XCTestCase {

    func test() {
        let components: [Component] = [ConcreteComponentA(), ConcreteComponentB()]

        print("The client code works with all visitors via the base Visitor interface:\n")
        let visitor1 = ConcreteVisitor1()
        Client.clientCode(components: components, visitor: visitor1)

        print("\nIt allows the same client code to work with different types of visitors:\n")
        let visitor2 = ConcreteVisitor2()
        Client.clientCode(components: components, visitor: visitor2)
    }
}

The client code works with all visitors via the base Visitor interface:

A + ConcreteVisitor1

B + ConcreteVisitor1


It allows the same client code to work with different types of visitors:

A + ConcreteVisitor2

B + ConcreteVisitor2

import Foundation
import XCTest


protocol Notification: CustomStringConvertible {

    func accept(visitor: NotificationPolicy) -> Bool
}

struct Email {

    let emailOfSender: String

    var description: String { return "Email" }
}

struct SMS {

    let phoneNumberOfSender: String

    var description: String { return "SMS" }
}

struct Push {

    let usernameOfSender: String

    var description: String { return "Push" }
}

extension Email: Notification {

    func accept(visitor: NotificationPolicy) -> Bool {
        return visitor.isTurnedOn(for: self)
    }
}

extension SMS: Notification {

    func accept(visitor: NotificationPolicy) -> Bool {
        return visitor.isTurnedOn(for: self)
    }
}

extension Push: Notification {

    func accept(visitor: NotificationPolicy) -> Bool {
        return visitor.isTurnedOn(for: self)
    }
}


protocol NotificationPolicy: CustomStringConvertible {

    func isTurnedOn(for email: Email) -> Bool

    func isTurnedOn(for sms: SMS) -> Bool

    func isTurnedOn(for push: Push) -> Bool
}

class NightPolicyVisitor: NotificationPolicy {

    func isTurnedOn(for email: Email) -> Bool {
        return false
    }

    func isTurnedOn(for sms: SMS) -> Bool {
        return true
    }

    func isTurnedOn(for push: Push) -> Bool {
        return false
    }

    var description: String { return "Night Policy Visitor" }
}

class DefaultPolicyVisitor: NotificationPolicy {

    func isTurnedOn(for email: Email) -> Bool {
        return true
    }

    func isTurnedOn(for sms: SMS) -> Bool {
        return true
    }

    func isTurnedOn(for push: Push) -> Bool {
        return true
    }

    var description: String { return "Default Policy Visitor" }
}

class BlackListVisitor: NotificationPolicy {

    private var bannedEmails = [String]()
    private var bannedPhones = [String]()
    private var bannedUsernames = [String]()

    init(emails: [String], phones: [String], usernames: [String]) {
        self.bannedEmails = emails
        self.bannedPhones = phones
        self.bannedUsernames = usernames
    }

    func isTurnedOn(for email: Email) -> Bool {
        return bannedEmails.contains(email.emailOfSender)
    }

    func isTurnedOn(for sms: SMS) -> Bool {
        return bannedPhones.contains(sms.phoneNumberOfSender)
    }

    func isTurnedOn(for push: Push) -> Bool {
        return bannedUsernames.contains(push.usernameOfSender)
    }

    var description: String { return "Black List Visitor" }
}



class VisitorRealWorld: XCTestCase {

    func testVisitorRealWorld() {

        let email = Email(emailOfSender: "some@email.com")
        let sms = SMS(phoneNumberOfSender: "+3806700000")
        let push = Push(usernameOfSender: "Spammer")

        let notifications: [Notification] = [email, sms, push]

        clientCode(handle: notifications, with: DefaultPolicyVisitor())

        clientCode(handle: notifications, with: NightPolicyVisitor())
    }
}

extension VisitorRealWorld {

    /// يجتاز كود العميل الإشعارات مع الزوار ويتحقق مما إذا كان
    /// الإشعار في القائمة السوداء وهل يجب عرضه وفقاً
    /// لـ SilencePolicy الحالي

    func clientCode(handle notifications: [Notification], with policy: NotificationPolicy) {

        let blackList = createBlackList()

        print("\nClient: Using \(policy.description) and \(blackList.description)")

        notifications.forEach { item in

            guard !item.accept(visitor: blackList) else {
                print("\tWARNING: " + item.description + " is in a black list")
                return
            }

            if item.accept(visitor: policy) {
                print("\t" + item.description + " notification will be shown")
            } else {
                print("\t" + item.description + " notification will be silenced")
            }
        }
    }

    private func createBlackList() -> BlackListVisitor {
        return BlackListVisitor(emails: ["banned@email.com"],
                                phones: ["000000000", "1234325232"],
                                usernames: ["Spammer"])
    }
}

Client: Using Default Policy Visitor and Black List Visitor
    Email notification will be shown
    SMS notification will be shown
    WARNING: Push is in a black list

Client: Using Night Policy Visitor and Black List Visitor
    Email notification will be silenced
    SMS notification will be shown
    WARNING: Push is in a black list
```

### typescript

```typescript
/**
 * تُعلن واجهة المكوّن عن أسلوب `accept` يجب أن يأخذ
 * واجهة الزائر الأساسية كوسيطة.
 */
interface Component {
    accept(visitor: Visitor): void;
}

/**
 * يجب أن يُنفِّذ كل مكوّن ملموس أسلوب `accept` بطريقة
 * تستدعي أسلوب الزائر المقابل لفئة المكوّن.
 */
class ConcreteComponentA implements Component {
    /**
     * لاحظ أننا ندعو `visitConcreteComponentA`، الذي يطابق
     * اسم الفئة الحالية. بهذه الطريقة نُعلم الزائر بفئة
     * المكوّن الذي يعمل معه.
     */
    public accept(visitor: Visitor): void {
        visitor.visitConcreteComponentA(this);
    }

    /**
     * قد تحتوي المكوّنات الملموسة على أساليب خاصة غير موجودة في
     * فئتها الأساسية أو واجهتها. لا يزال الزائر قادراً على استخدام
     * هذه الأساليب لأنه على دراية بالفئة الملموسة للمكوّن.
     */
    public exclusiveMethodOfConcreteComponentA(): string {
        return 'A';
    }
}

class ConcreteComponentB implements Component {
    /**
     * نفس الأمر هنا: visitConcreteComponentB => ConcreteComponentB
     */
    public accept(visitor: Visitor): void {
        visitor.visitConcreteComponentB(this);
    }

    public specialMethodOfConcreteComponentB(): string {
        return 'B';
    }
}

/**
 * تُعلن واجهة الزائر عن مجموعة من أساليب الزيارة المقابلة لفئات المكوّن.
 * يتيح توقيع أسلوب الزيارة للزائر تحديد الفئة الدقيقة
 * للمكوّن الذي يتعامل معه.
 */
interface Visitor {
    visitConcreteComponentA(element: ConcreteComponentA): void;

    visitConcreteComponentB(element: ConcreteComponentB): void;
}

/**
 * تُنفِّذ الزوار الملموسون عدة إصدارات من نفس الخوارزمية التي يمكنها
 * العمل مع جميع فئات المكوّن الملموسة.
 *
 * يمكنك الاستفادة من أكبر ميزة لنمط الزائر عند استخدامه مع هيكل كائن معقد،
 * مثل شجرة Composite. في هذه الحالة، قد يكون من المفيد تخزين بعض
 * الحالة الوسيطة للخوارزمية أثناء تنفيذ أساليب الزائر على كائنات مختلفة
 * من الهيكل.
 */
class ConcreteVisitor1 implements Visitor {
    public visitConcreteComponentA(element: ConcreteComponentA): void {
        console.log(`${element.exclusiveMethodOfConcreteComponentA()} + ConcreteVisitor1`);
    }

    public visitConcreteComponentB(element: ConcreteComponentB): void {
        console.log(`${element.specialMethodOfConcreteComponentB()} + ConcreteVisitor1`);
    }
}

class ConcreteVisitor2 implements Visitor {
    public visitConcreteComponentA(element: ConcreteComponentA): void {
        console.log(`${element.exclusiveMethodOfConcreteComponentA()} + ConcreteVisitor2`);
    }

    public visitConcreteComponentB(element: ConcreteComponentB): void {
        console.log(`${element.specialMethodOfConcreteComponentB()} + ConcreteVisitor2`);
    }
}

/**
 * يمكن لكود العميل تشغيل عمليات الزائر على أي مجموعة من العناصر دون
 * معرفة فئاتها الملموسة. تُوجِّه عملية القبول الاستدعاء إلى
 * العملية المناسبة في كائن الزائر.
 */
function clientCode(components: Component[], visitor: Visitor) {
    // ...
    for (const component of components) {
        component.accept(visitor);
    }
    // ...
}

const components = [
    new ConcreteComponentA(),
    new ConcreteComponentB(),
];

console.log('The client code works with all visitors via the base Visitor interface:');
const visitor1 = new ConcreteVisitor1();
clientCode(components, visitor1);
console.log('');

console.log('It allows the same client code to work with different types of visitors:');
const visitor2 = new ConcreteVisitor2();
clientCode(components, visitor2);

The client code works with all visitors via the base Visitor interface:
A + ConcreteVisitor1
B + ConcreteVisitor1

It allows the same client code to work with different types of visitors:
A + ConcreteVisitor2
B + ConcreteVisitor2
```

