لماذا يقع على عاتق الزائر مسؤولية تعداد الأطفال في نمط الزائر؟

StackOverflow https://stackoverflow.com/questions/522803

سؤال

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

أعتقد أن السؤال الحقيقي هو: هل هو نمط يتم فيه التعداد بواسطة رمز الزيارة بدلاً من رمز الزائر؟

هل كانت مفيدة؟

المحلول

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

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

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

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

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

نصائح أخرى

نعم. يمكن الكائنات زار تفعل التعداد (أي مكالمة على الأطفال الحاجة). هذا لا يزال يسمى "الزوار" نمط (في الواقع، <م> تصميم نمط الصورة العينة الأولى من الزوار يفعل بهذه الطريقة). بلدي صنعي سبيل المثال المتكررة:

public void accept(Visitor visitor) {
  for (Node n : children) {
    n.accept(visitor);
  }
}

ملحوظة: لزيارة الأطفال، لا نستطيع أن نقول visitor.visit(n);. وذلك لأن جافا لا حيوي حدد طريقة (استنادا إلى الفئة وقت التشغيل الوسائط الخاصة بها)، ولكن يختار أسلوب ثابت (حسب نوع وقت الترجمة الوسائط الخاصة بها).

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

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

interface Visitable {
    void accept(Visitor visitor);
}

class WorkingRoom implements Visitable {
    public int number;
    WorkingRoom(int number) {
        this.number = number;
    }

    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

class BossRoom implements Visitable {
    public String bossName;
    BossRoom(String bossName) {
        this.bossName = bossName;
    }
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

interface Visitor{
    void visit(WorkingRoom workingRoom);
    void visit(BossRoom bossRoom);
    void visit(Office office);
}

class Office implements Visitable{
    public Visitable[] firstFloor;
    public Visitable[] secondFloor;
    public Visitable ceoRoom;
    public Office(){
        firstFloor = new Visitable[]{ new WorkingRoom(101),
                                        new WorkingRoom(102),
                                        new BossRoom("Jeff Atwood"),
                                        new WorkingRoom(103)};
        secondFloor = new Visitable[]{  new WorkingRoom(201),
                                        new WorkingRoom(202),
                                        new BossRoom("Joel Spolsky")};

        ceoRoom = new BossRoom("Bill Gates");
    }

    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public void showMeTheOffice(Visitor visitor, boolean sayPlease) {
        // Office manager decides the order in which rooms are visited
        for(int i=secondFloor.length-1; i >= 0; i--){
            secondFloor[i].accept(visitor);
        }
        if (sayPlease){
            ceoRoom.accept(visitor);
        }
        for (int i = 0; i < firstFloor.length; i++) {
            firstFloor[i].accept(visitor);
        }
    }
}

class Insider implements Visitor{
    public void visit(WorkingRoom workingRoom) {
        System.out.println("I> This is working room #"+workingRoom.number);
    }

    public void visit(BossRoom bossRoom) {
        System.out.println("I> Hi, "+bossRoom.bossName);
    }

    public void visit(Office office) {
        // I know about office structure, so I'll just go to the 1st floor
        for(int i=0;i<office.firstFloor.length;i++){
            office.firstFloor[i].accept(this);
        }
    }
}

class Outsider implements Visitor{

    public void visit(Office office) {
        // I do not know about office structure, but I know they have a 
        // nice office manager
        // I'll just ask to show me the office
        office.showMeTheOffice(this, true);
    }

    public void visit(WorkingRoom workingRoom) {
        System.out.println("O> Wow, room #"+workingRoom.number);
    }

    public void visit(BossRoom bossRoom) {
        System.out.println("O> Oh, look, this is "+bossRoom.bossName);
    }
}

public class Main{
    public static void main(String[] args) {
        Office office = new Office(); // visited structure
        // visitor who knows about office structure
        Insider employee = new Insider(); 
        office.accept(employee);
        System.out.println();
        // visitor who does not know about exact office structure
        // but knows something else
        Outsider candidate = new Outsider(); 
        office.accept(candidate);

        // no enumeration at all, but still a visitor pattern
        Visitable v = new BossRoom("Linus Torvalds");
        v.accept(candidate);
    }
}

وكان مشروع مع استخدام واسع من نمط الزوار دون أي تعداد على الإطلاق. كان لدينا واجهة قاعدة الميدانية والعديد من الفئات تنفيذها، مثل StringField، NumberField، وما إلى ذلك في كثير من الأحيان كان علينا أن نفعل أشياء مختلفة استنادا إلى نوع الحقل، على سبيل المثال جعله بطريقة مختلفة، وتحميل من DB، تصدير إلى XML، الخ ونحن يمكن تحديد أساليب في واجهة الميدان، ولكن هذا سيجعل من جانب كل ميزة واحدة لهذا المشروع - حقل الفقراء أن يعرف حول تصدير واستيراد، وجعلها إلى html والخانات، وغيرها ونحن أيضا يمكن أن تستخدم instanceof لكن مجموعة من ممكن تم تغيير الفصول تنفيذ واجهة الميدان مع مرور الوقت وكان من الممكن لإضافة نوع حقل جديد وتنس أن تضيف

else if (field instanceof NewlyAddedFieldType) {...}

وفي مكان ما. لذلك قررنا استخدام نمط الزوار، وكان مثل

Visitor v = new XMLExportVisitor(outputStream);
field.accept(v);

وكما هو مطلوب أي التنفيذ الميداني لديك طريقة

void accept(FieldVisitor visitor)

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

visitor.visit(this);

وحيث هذه هي فئة المضافة حديثا. هذا يفرض لي أن أضيف

void visit(NewlyAddedClass visited);

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

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

وإذا نظرنا إلى الوراء إلى بلدي oofRep الزوار كان لديها سلسلة من المستويات فئات مختلفة لزيارة وكان التكرار في أساليب مثل:

void
oofRepVisitor::VisitViewHeaders(oofRepBandList& inBands)
{
    VisitBandList(inBands);
}


void
oofRepVisitor::VisitBandList(oofRepBandList& inBands)
{
    EnterLevel();
    const unsigned long numBands = inBands.count();
    for (unsigned long i=0; i<numBands; i++) {
        oofRepBand* theBand = inBands.value(i);
        assert(theBand);
        VisitTypedBand(theBand);
    }
    LeaveLevel();
}

ومع تجاوز

void
OOF_repXMLlayoutVisitor::VisitViewHeaders(oofRepBandList& inBands)
{
    oofRepStreamEnv::out() << mIdentities.getIndentString();
    if (inBands.keepTogether())
        oofRepStreamEnv::out()  << "<header>\n";    
    else  // default is ON, and simplifies XML
        oofRepStreamEnv::out()  << "<header keepTogether='false'>\n";
    VisitBandList(inBands);
    oofRepStreamEnv::out() 
        << mIdentities.getIndentString()
        << "</header>\n";
}

وإلقاء نظرة على التفسير في هذه المقالة .

المعرفة :

<اقتباس فقرة>   

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top