Pergunta

Com base no código que eu encontrei, parece que o visitante é obrigado a conhecer a estrutura dos objetos visitados e chamada sobre as crianças necessárias. Este parece ser um desajeitado pouco em alguns casos onde o visitante gostaria de continuar a trabalhar, mesmo que as classes visitadas são modificados.

Eu acho que a verdadeira questão é: É a sua padrão em que a enumeração é feito pelo código visitou em vez do código visitante

?
Foi útil?

Solução

O objeto visitante é necessário para conhecer a estrutura das coisas que ele visita. Isso é OK, no entanto. Você está deveria para escrever operações visita especializados para cada tipo de coisa que o visitante saiba como visitar. Isso permite que o visitante a decidir o quanto ele realmente quer visitar, e em que ordem.

Suponha que você tenha uma árvore. Um visitante pode fazer um percurso pré-ordem, pode-se fazer uma travessia em ordem, e ainda um outro visitante pode agir apenas em nós folha. As classes visitante pode fazer todas essas coisas sem a necessidade de quaisquer alterações à classe de árvore.

O visitante conhece a estrutura, mas isso não significa necessariamente que a operação executa o visitante conhece toda a estrutura. Você pode combinar um visitante com um comando . Dê o objeto visitante um objeto de comando, e que o visitante irá invocar o comando em cada coisa que visitas.

Se você quer ter uma operação simples e deixe a coleção dar-lhe cada item de agir sobre, então você quer a coleção para fornecer um iterador por si. Chamar sua função em cada coisa que o iterador lhe dá.

Se você quiser iterar sobre os nós da árvore em várias ordens, então a árvore terá de oferecer vários iteradores. Se você quiser nós de processo em uma ordem que a árvore já não apoiar, você precisará modificar a classe de árvore.

Outras dicas

Sim. Os objetos visitados pode fazer a enumeração (call ou seja, sobre as crianças necessárias). Este ainda é chamado o padrão de "Visitante" (na verdade, Design Pattern é primeira amostra de visitante faz-lo desta forma). Meu exemplo de trecho fez-up:

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

Nota: para visitar as crianças, não podemos dizer visitor.visit(n);. Isso ocorre porque Java não seleciona dinamicamente o método (com base na classe de tempo de execução de seus argumentos), mas seleciona o método estaticamente (pelo tipo de tempo de compilação de seus argumentos).

Em poucas palavras, eu acho que o padrão Visitor é ortogonal à maneira enumeração é feito. Isso pode ser feito de qualquer forma, ou nenhuma enumeração em tudo.

Eu acho que o visitante é obrigado a saber o que elementos estrutura visitou consiste. Como saber que carro é composto por rodas e motor. Para saber exatamente como eles são combinados não é necessário, eu acho. Considere seguinte exemplo. Insider sabe visitou estrutura do objeto e executa enumeração si. Outsider não sabe disso e delegados enumeração para o objeto visitado.

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

Eu tinha um projeto com ampla utilização de padrão do visitante sem qualquer enumeração de todo. Tivemos base de interface de campo e muitas classes de implementação, como StringField, NumberField, etc. Muitas vezes tivemos que fazer coisas diferentes com base no tipo de campo, por exemplo, torná-lo de uma maneira diferente, a carga de DB, exportar para xml, etc . Poderíamos definir métodos de interface de campo, mas que tornaria juntamente com cada característica do projeto - pobre campo tem de saber sobre exportação, importação, tornando a html e rtf, etc. Nós também pode usar instanceof mas conjunto de possível as classes que implementam interface de campo foi mudado ao longo do tempo e foi possível adicionar novo tipo de campo e esquecer de adicionar

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

em algum lugar. Então decidimos usar padrão do visitante, e foi como

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

Como qualquer implementação campo é obrigatório ter método

void accept(FieldVisitor visitor)

, em seguida, se eu adicionar nova implementação de interface de campo, eu tenho que implementar isso de alguma forma. Normalmente é

visitor.visit(this);

onde esta é uma classe recém-adicionado. Este forças me adicionar

void visit(NewlyAddedClass visited);

para a interface FieldVisitor, o que me faz implementá-lo em todas as implementações FieldVisitor que já temos. Então, se eu esquecer de fazer algo deste - Vou pegar erro do compilador. Enumeração neste caso, se houver, foi feito fora da estrutura visitado e visitante. Mas eu ainda penso nisso como um caso válido de padrão do visitante. Ele aconteceu para ser um pouco mais difícil de implementar, mas mais fácil e seguro para uso.

O padrão hierárquico Visitor explica uma abordagem diferente, onde ele adiciona eventos de entrada e saída níveis. O associado página de discussão argumentos apresenta para iteração estar dentro do visitante ou do recipiente. Ele inclui uma sugestão de usar um iterador externo que faz sentido para mim se você tem uma árvore regular e necessidade de iterate diferente.

Olhando para trás ao meu oofRep visitante que tinha uma série de níveis de classes diferentes para visitar e teve a iteração dentro de métodos como:

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

com uma substituição

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";
}

Tenha um olhar para a explicação neste artigo .

A partir Wiki :

Na programação orientada a objeto e engenharia de software, o visitante padrão de design é uma maneira de separar um algoritmo a partir de uma estrutura de objecto em que opera. A prática resultado desta separação é a capacidade de adicionar novas operações para estruturas objecto existente, sem modificar essas estruturas. Portanto, usando o padrão do visitante ajuda conformidade com o aberto / fechado princípio.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top