2012-04-12 22 views
23

Así que he leído toda la documentación sobre el patrón de visitante, y todavía estoy muy confundido. Tomé este ejemplo de otra pregunta SO, ¿alguien podría ayudarme a entender? Por ejemplo, ¿cuándo usamos un patrón de diseño de visitante? Creo que pude haber entendido algo de eso, pero no puedo ver el panorama completo. ¿Cómo sé cuándo puedo usarlo?Explicación del patrón de visitante

class equipmentVisited 
{ 
    virtual void accept(equipmentVisitor* visitor) = 0; 
} 

class floppyDisk : public equipmentVisited 
{ 
    virtual void accept(equipmentVisitor* visitor); 
} 

class processor : public equipmentVisited 
{ 
    virtual void accept(equipmentVisitor* visitor); 
} 

class computer : public equipmentVisited 
{ 
    virtual void accept(equipmentVisitor* visitor); 
} 

class equipmentVisitor 
{ 
    virtual void visitFloppyDisk(floppyDisk*); 
    virtual void visitProcessor(processor*); 
    virtual void visitComputer(computer*); 
} 

// Some additional classes inheriting from equipmentVisitor would be here 

equipmentVisited* visited; 
equipmentVisitor* visitor; 

// Here you initialise visited and visitor in any convenient way 

visited->accept(visitor); 
+1

para un ejemplo completo, ver el ejemplo en java http://en.wikipedia.org/wiki/Visitor_pattern#Java_example – stefaanv

+0

En ese ejemplo se podría hacer fácilmente equipmentVisitor sólo utilizan la visita función, con tres funciones sobrecargadas cada uno toma una equipemtVisited clase derivada diferente como el parámetro. Solo yo haría de esa clase un Visitador de equipos. –

Respuesta

25

patrón de Visitantes se utiliza para implementar double dispatch. En palabras simples, significa que el código que se ejecuta depende de los tipos de tiempo de ejecución de dos objetos.

Cuando se llama a un función virtual regulares, es un despacho único: el trozo de código que se ejecuta depende del tipo de ejecución de un único objeto, a saber, el método virtual del que realiza la llamada.

Con el patrón de visitante, el método que se está llamando en última instancia depende del tipo de dos objetos - el tipo del objeto de la aplicación de la equipmentVisitor, y el tipo de objeto sobre el que se llama a accept (es decir, el equipmentVisited subclase) .

Existen otras formas de implementar el envío doble en C++. El artículo 31 de Scott Meyer's "More Effective C++" trata este tema en profundidad.

+1

Entonces, ¿es como cualquier subclase de equipos vistos puede llamar a la función 'aceptar' de cualquiera de las subclases de equipmentVistor? ¿Es eso correcto? –

+0

@dasblinkenlight - ¡Nunca "conseguí" el patrón de visitante! Todavía lucha con eso. Por lo tanto, responda a esta pregunta: Tengo una clase maestra que hereda de una clase de Cliente. De esta forma, el Maestro puede hacer todo lo que el Cliente puede hacer, además de algunas cosas más. Si quiero utilizar estas clases de forma polimórfica, entonces necesito una clase base genérica. PROBLEMA: Ahora mi clase magistral hereda de dos clases. ¿Crees que el patrón de visitante podría resolver mi problema? – Patricia

+0

@Patricia ¿Por qué necesitarías una clase base genérica? ¿Por qué no puedes usar '' Cliente' como el puntero base/tipo de referencia? El polimorfismo funciona con cualquier forma de herencia, no solo con el patrón restringido de 'interfaz'. 'Master' hereda de' Client', por lo que se puede usar un puntero o referencia-a -Cliente' para invocar métodos 'virtuales' en un' Master'. Pensar que necesitas agregar otra clase base en la mezcla es erróneo y un golpe totalmente redundante a la eficiencia. Ese es el problema. –

13

Creo que el nombre del visitante patrón es bastante desafortunado. En lugar de la palabra visitante, diría Functor u Operador y en lugar de 'visitar' diría 'aplicar'.

Mi comprensión del patrón de visitante es el siguiente:

En plantilla de meta-programación (STL/Boost) (compilar vinculante tiempo) se puede lograr (el diseño ortogonal) las separaciones de las operaciones de las estructuras, por el medio de objetos función (Functors.) Por ejemplo, en

template <class RandomAccessIterator, class Compare> 
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp); 

el borrador es un funtor/operador que representa 'menor que' la operación de una manera muy genérica, por lo que no tiene que tener muchas variantes de especie función:

Para patrón de visitante desea lograr algo similar, pero en el caso de encuadernación (tardía) de tiempo de ejecución:

Quiere simplificar la interfaz de A, desea mantener la posibilidad de futuras extensiones (las nuevas operaciones trabajan con A) y desea lograr la estabilidad de la interfaz de A en el caso de esas extensiones.

Desde el original 'grasa' de clase:

class A 
{ 
    public: 
    virtual void function_or_operation_1();//this can be implemented in terms of public interface of the other functions 
    virtual void function_or_operation_2(); 
    //..etc 

    virtual void function_or_operation_N(); 
    public: 
    //stable public interface, some functions of procedures 

    private: 
    //.... 
} 

que eliminar el mayor número de función de interfaz pública como sea posible (siempre y cuando se pueden implementar en términos de las funciones no extraídos de la misma interfaz pública) y representan las operaciones como los objetos functor u objetos de una nueva jerarquía Functor:

se reduce el número de la función en la clase base a por tener interfaz muy genérico utilizando Functor_or_Operator hacia adelante declaró:

class Functor_or_Operator; 
class A 
{ 
    public: 
    virtual void apply(Functor_or_Operator*);//some generic function operates on this objects from A hierarchy 
    //..etc 
    public: 
    //stable public interface, some functions 

    private: 
    //.... 
} 

// Ahora tiene N (= 3) clases en una jerarquía (A, B, C) y M operaciones o funciones representadas por clases en la jerarquía Functor_or_Operator Debe implementar N * M definiciones de cómo cada la operación de Functor_or_Operator funciona en todas las clases en la jerarquía A. Lo importante es que puede hacerlo sin cambiar la interfaz de la clase 'A'. La declaración de la clase 'A' se vuelve muy estable en el caso de las nuevas incorporaciones al introducir nuevas operaciones o funciones que trabajan con objetos de una jerarquía o en el caso de nuevas clases derivadas en la jerarquía A. La estabilidad de A (sin cambios en A) en presencia de adiciones es importante para evitar la costosa (y algunas veces imposible) recompilación de software que incluye encabezados de A en muchos lugares.

Para cada clase nueva en la jerarquía A, extiende la definición de Functor_or_Operator base, agrega nuevos archivos de implementación, pero nunca necesita tocar el encabezado de la clase base A (generalmente clase de interfaz o abstracta).

class Functor_or_Operator 
    { 
    virtual void apply(A*)=0; 
    virtual void apply(B*)=0; 
    virtual void apply(C*)=0; 
    } 

    void A::apply(Functor_or_Operator* f) 
    { f->apply(this);} //you need this only if A is not abstract (it is instantiable) 

    class B:public A 
    { 
    public: 
    void apply(Functor_or_Operator* f) { f->apply(this);} //dynamic dispatch , you call polymhorphic Functor f on this object 
    //..the rest of B implementation. 
    } 

    class C:public A 
    { 
    public: 
    void apply(Functor_or_Operator* f) { f->apply(this);} //dynamic dispatch , you call polymorfic Functor f on this object 
    //..the rest of C implementation. 
    } 

    class Functor_or_Operator_1:public Functor_or_Operator 
    { 
    public: 
     //implementations of application of a function represented by Functor_or_Operator_1 on each A,B,C 
     void apply(A*) {}//(only if A is instantiable,not an abstract class) 
     void apply(B*) {} 
     void apply(C*) {} 
    } 

    class Functor_or_Operator_2:public Functor_or_Operator 
    { 
    public: 
     //implementations of application of a function represented by Functor_or_Operator_2 on each A,B,C 
     void apply(A*) {}//(only if A is instantiable,not an abstract class) 
     void apply(B*) {} 
     void apply(C*) {} 
    } 
Cuestiones relacionadas