strategy source que programacion patterns pattern patron objetivo making estrategia ejemplo c++ visitor

c++ - source - Explicación del patrón de visitante



strategy programacion (2)

Creo que el nombre del patrón Visitante 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 la siguiente:

En la meta-programación de plantillas (STL / BOOST) (enlace de tiempo de compilación) puede lograr (el diseño ortogonal) las separaciones de operaciones de estructuras, mediante los objetos de función (Functors). Por ejemplo, en

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

la comp es un functor / operador que representa la operación "menor que" de una manera muy genérica, por lo que no tiene que tener muchas variantes de la función de clasificación:

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

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

De la clase original de "grasa":

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: //.... }

elimina la mayor cantidad posible de funciones de la interfaz pública (siempre que se puedan implementar en términos de las funciones no extraídas de la misma interfaz pública) y represente las operaciones como los objetos de functor u objetos de una nueva jerarquía de Functor:

Reduce el número de funciones en la clase base A al tener una interfaz muy genérica utilizando el Functor_or_Operator declarado hacia adelante:

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 jerarquía A (A, B, C) y M operaciones o funciones representadas por clases en Functor_or_Operator hierarchy Usted necesita implementar N * M definiciones de cómo cada operación de Functor_or_Operator funciona en cada clase en una jerarquía. Lo importante es que puedes 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 cuando se introducen nuevas operaciones o funciones que trabajan con objetos de la jerarquía 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 compilación costosa (ya veces imposible) de software que incluye encabezados de A en muchos lugares.

Para cada nueva clase 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 interfaz o clase 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*) {} }

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 de SO, ¿podría alguien ayudarme a entender? Por ejemplo, ¿cuándo utilizamos un patrón de diseño de visitante? Creo que puede que haya entendido algo, pero no puedo ver el panorama completo. ¿Cómo puedo saber cuándo puedo usarlo?

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


Patrón de visitante se utiliza para implementar doble despacho . 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 una función virtual normal, se trata de un solo envío: el fragmento de código que se ejecuta depende del tipo de tiempo de ejecución de un solo objeto, es decir, el método virtual al que está llamando.

Con el patrón de visitante, el método al que se llama depende en última instancia del tipo de dos objetos: el tipo de objeto que implementa equipmentVisitor y el tipo de objeto al que llama call (es decir, la subclase de equipmentVisited ).

Hay otras formas de implementar doble despacho en C ++. El artículo 31 de "Meyer C ++ más efectivo" de Scott Meyer trata este tema en profundidad.