Visitante (patrón de diseño)

La versión actual de la página aún no ha sido revisada por colaboradores experimentados y puede diferir significativamente de la versión revisada el 4 de enero de 2016; las comprobaciones requieren 26 ediciones .
Visitante
Visitante
Tipo de conductual
Objetivo sin cambiar la clase principal , agregue nuevas operaciones.
Estructura
Se aplica en casos cuando es necesario realizar una operación similar (misma) para varias clases.
ventajas
  • se agrega nueva funcionalidad a varias clases a la vez sin cambiar el código de estas clases;
  • le permite obtener información sobre el tipo de un objeto;
  • programación doble;
  • posibilidad de describir un algoritmo propio para cada tipo de objetos .
menos
  • al cambiar la clase de servicio, debe cambiar el código de plantilla;
  • es difícil agregar nuevas clases, porque la jerarquía del visitante y sus hijos necesita ser actualizada.
Descrito en Patrones de diseño

Un  visitante es un patrón de diseño de comportamiento que describe una operación que se realiza en objetos de otras clases. Cuando cambia de visitante, no hay necesidad de cambiar las clases de servicio .

La plantilla demuestra el método clásico de recuperación de información de tipos perdidos sin recurrir al envío doble de downcast .

Problema resuelto

Debe realizar algunas operaciones desconectadas en varios objetos, pero debe evitar contaminar su código. Y no hay forma ni deseo de consultar el tipo de cada nodo y lanzar el puntero al tipo correcto antes de realizar la operación deseada.

Reto

Se realizan una o más operaciones en cada objeto de alguna estructura. Debe definir una nueva operación sin cambiar las clases de objeto.

Solución

Por independencia, el visitante tiene una jerarquía separada. Las estructuras tienen una cierta interfaz de interacción.

Uso

Si existe la posibilidad de que la jerarquía de clases de servicio cambie, sea inestable o la interfaz pública sea lo suficientemente eficiente para que la plantilla acceda, entonces su uso es malicioso.

Se crea una clase base Visitorcon métodos visit()para cada subclase del padre Element. Agregue un método accept(visitor)a la jerarquía de elementos. Para cada operación que se debe realizar en los objetos Element, deriva una Visitorclase de. Las implementaciones de métodos visit()deben usar la interfaz pública de la clase Element. Como resultado: los clientes crean objetos Visitory los pasan a cada objeto Elementllamando a accept().

Recomendaciones

La plantilla debe usarse si:

Ventajas y desventajas

Beneficios :

Desventajas :

Implementación

  1. Agregue un método accept(Visitor)a la jerarquía de "elementos".
  2. Cree una clase base Visitory defina métodos visit()para cada tipo de elemento.
  3. Cree clases derivadas Visitorpara cada operación realizada en los elementos.
  4. El cliente crea un objeto Visitory lo pasa al método llamadoaccept().

C++

Ejemplo de implementación en C++ #incluir <iostream> #incluir <cadena> clase Foo ; barra de clases ; clase Bas ; visitante de clase { público : visita de vacío virtual ( Foo & ref ) = 0 ; visita nula virtual ( Bar & ref ) = 0 ; visita de vacío virtual ( Baz & ref ) = 0 ; virtual ~ Visitante () = predeterminado ; }; elemento de clase { público : aceptación de vacío virtual ( Visitante & v ) = 0 ; elemento ~ virtual () = predeterminado ; }; clase Foo : elemento público { público : anular aceptar ( Visitante & v ) anular { v . visitar ( * esto ); } }; Barra de clase : elemento público { público : anular aceptar ( Visitante & v ) anular { v . visitar ( * esto ); } }; clase Baz : elemento público { público : anular aceptar ( Visitante & v ) anular { v . visitar ( * esto ); } }; clase GetType : visitante público { público : std :: valor de cadena ; _ público : anular visita ( Foo & ref ) anular { valor = "foo" ; } anular visita ( barra y referencia ) anular { valor = "barra" ; } anular visita ( Baz & ref ) anular { valor = "base" ; } }; int principal () { Foo Foo ; barra de bar ; baz baz ; Elemento * elementos [] = { & foo , & bar , & baz }; para ( auto elem : elementos ) { Visitante GetType ; elem -> aceptar ( visitante ); std :: cout << visitante . valor << std :: endl ; } devolver 0 ; }

Java

Ejemplo de implementación de Java Demostración de clase pública { public static void main ( String [] args ) { Point p = new Point2d ( 1 , 2 ); Visitante v = nuevo Chebyshev (); pág . aceptar ( v ); sistema _ fuera _ println ( p . getMetric () ); } } Visitante de interfaz { visita pública nula ( Point2d p ); visita de vacío público ( Point3d p ); } punto de clase abstracta { public abstract void accept ( Visitante v ); métrica doble privada = - 1 ; getMetric public double () { return metric ; } public void setMetric ( doble métrica ) { this . métrica = métrica ; } } class Point2d extiende Point { public Point2d ( double x , double y ) { this . x = x ; esto _ y = y _ } public void accept ( Visitante v ) { v . visitar ( esto ); } privada doble x ; getX público doble () { return x ; } privado doble y ; public double getY () { return y ; } } class Point3d extiende Point { public Point3d ( double x , double y , double z ) { this . x = x ; esto _ y = y _ esto _ z = z _ } public void accept ( Visitante v ) { v . visitar ( esto ); } privada doble x ; getX público doble () { return x ; } privado doble y ; public double getY () { return y ; } privado doble z ; public double getZ () { return z ; } } class Euclid implementa Visitor { public void visit ( Point2d p ) { p . setMetric ( Math . sqrt ( p . getX () * p . getX () + p . getY () * p . getY () ) ); } visita public void ( Point3d p ) { p . setMetric ( Math . sqrt ( p . getX () * p . getX () + p . getY () * p . getY () + p . getZ () * p . getZ () ) ); } } class Chebyshev implementa Visitor { public void visit ( Point2d p ) { double ax = Math . abs ( p. getX ( ) ) ; doble -ay = Matemáticas . abs ( p . getY () ); pág . setMetric ( ax > ay ? ax : ay ); } visita public void ( Point3d p ) { double ax = Math . abs ( p. getX ( ) ) ; doble -ay = Matemáticas . abs ( p . getY () ); doble az = Matemáticas . abs ( p . getZ () ); doble max = ax > ay ? hacha : ay ; si ( max < az ) max = az ; pág . setMetric ( max ); } }

C#

Ejemplo de implementación en C# Demostración de clase estática pública { Principal vacío estático privado () { Punto p = nuevo Punto2D ( 1 , 2 ); IVisitor v = nuevo Chebyshev (); pág . aceptar ( v ); consola _ WriteLine ( p . Métrica ); } } interfaz interna IVisitor { void Visit ( Point2D p ); Visita nula ( Point3Dp ) ; } clase abstracta interna Point { public double Metric { get ; conjunto ; } = - 1 ; public abstract void Aceptar ( visitante IVisitor ); } clase interna Point2D : Point { public Point2D ( doble x , doble y ) { X = x ; y = y _ } público doble X { obtener ; } público doble Y { obtener ; } public override void Aceptar ( IVisitor visitante ) { visitante . visitar ( esto ); } } clase interna Point3D : Point { public Point3D ( doble x , doble y , doble z ) { X = x ; y = y _ Z = z _ } público doble X { obtener ; } público doble Y { obtener ; } público doble Z { obtener ; } public override void Aceptar ( IVisitor visitante ) { visitante . visitar ( esto ); } } clase interna Euclid : IVisitor { public void Visit ( Point2D p ) { p . Métrica = Matemáticas . Sqrt ( p . X * p . X + p . Y * p . Y ); } public void Visita ( Point3D p ) { p . Métrica = Matemáticas . Sqrt ( p . X * p . X + p . Y * p . Y + p . Z * p . Z ); } } clase interna Chebyshev : IVisitor { public void Visit ( Point2D p ) { var ax = Math . abdominales ( pág . X ); varay = Matemáticas ._ _ Abdominales ( p . Y ); pág . Métrica = ax > ay ? hacha : ay ; } public void Visit ( Point3D p ) { var ax = Math . abdominales ( pág . X ); varay = Matemáticas ._ _ Abdominales ( p . Y ); var az = Matemáticas . Abdominales ( pág . Z ); varmax = ax > ay ? _ hacha : ay ; si ( max < az ) max = az ; pág . Métrica = máx .; } }

PHP

Ejemplo de implementación en php <?php interfaz Visitante { visita de función pública ( Punto $ punto ); } punto de clase abstracta { función abstracta pública aceptar ( Visitante $ visitante ); privado $_métrica = - 1 ; public function getMetric () { return $this -> _metric ; } public function setMetric ( $metric ) { $this -> _metric = $metric ; } } clase Punto2d extiende Punto { public function __construct ( $x , $y ) { $this -> _x = $x ; $esto -> _y = $y ; } función pública aceptar ( Visitante $ visitante ) { $ visitante -> visita ( $ esto ); } privado $_x ; public function getX () { return $this -> _x ; } privado $_y ; public function getY () { return $this -> _y ; } } class Point3d extiende Point { public function __construct ( $x , $y , $z ) { $this -> _x = $x ; $esto -> _y = $y ; $esto -> _z = $z ; } función pública aceptar ( Visitante $ visitante ) { $ visitante -> visita ( $ esto ); } privado $_x ; public function getX () { return $this -> _x ; } privado $_y ; public function getY () { return $this -> _y ; } privado $_z ; public function getZ () { return $this -> _z ; } } class Euclid implementa Visitor { public function visit ( Point $p ) { if ( $p instancia de Point2d ) $p -> setMetric ( sqrt ( $p -> getX () * $p -> getX () + $p -> getY () * $p -> getY () ) ); elseif ( $p instancia de Point3d ) $p -> setMetric ( sqrt ( $p -> getX () * $p -> getX () + $p -> getY () * $p -> getY () + $p - > getZ () * $p -> getZ () ) ); } } class Chebyshev implementa Visitor { public function visit ( Point $p ) { if ( $p instancia de Point2d ){ $ax = abs ( $p -> getX () ); $ay = abs ( $p -> getY () ); $p -> setMetric ( $ax > $ay ? $ax : $ay ); } elseif ( $p instancia de Point3d ){ $ax = abs ( $p -> getX () ); $ay = abs ( $p -> getY () ); $az = abs ( $p -> getZ () ); $max = $ax > $ay ? $ax : $ay ; if ( $max < $az ) $max = $az ; $p -> setMetric ( $max ); } } } inicio de función (){ $p = nuevo Point2d ( 1 , 2 ); $v = nuevoChebyshev ( ); $p -> aceptar ( $v ); echo ( $p -> getMetric () ); }; inicio ();

Pitón

Ejemplo de implementación en Python de abc import ABCMeta , método abstracto de escribir import List clase Espía ( metaclase = ABCMeta ): """ Espía visitante """ @abstractmethod def visit_military_base ( self , military_base : 'MilitaryBase' ) -> Ninguno : """ Visitar la base militar de la marina """ pase @abstractmethod def visit_headquarters ( self , cuartel general : 'Cuartel general' ) -> Ninguno : """ Visitar el cuartel general del ejército """ pasar class MilitaryFacility ( metaclase = ABCMeta ): """ Instalación militar - instalación visitada """ @abstractmethod def accept ( self , spy : Spy ) -> Ninguno : """ Aceptar visitante espía """ pasar clase MilitaryBase ( MilitaryFacility ): """ base militar submarina """ def __init__ ( self ) -> Ninguno : self . _secret_draftings = 1 yo . _submarinos_nucleares = 1 def __repr__ ( self ) -> str : return 'La base militar tiene {} submarinos nucleares y {} planos secretos' . formato ( self . _nuclear_submarines , self . _secret_draftings ) def accept ( self , spy : Spy ) -> Ninguno : spy . visit_military_base ( auto ) def remove_secret_draftings ( self ) -> Ninguno : si es self . _secret_draftings : self . _redacciones_secretas -= 1 def remove_nuclear_submarine ( self ) -> Ninguno : si es self . _submarinos_nucleares : auto . _submarinos_nucleares -= 1 @property def is_combat_ready ( self ) -> bool : return self . _submarinos_nucleares > 0 clase Cuartel General ( MilitaryFacility ): """ Cuartel General del Ejército """ def __init__ ( self ) -> Ninguno : self . _generales = 3 auto . _documentos_secretos = 2 def __repr__ ( self ) -> str : return 'Hay {} generales y {} documentos secretos en el cuartel general ' . formato ( self . _generales , self . _secret_documents ) def accept ( self , spy : Spy ) -> Ninguno : spy . visit_sede central ( auto ) def remove_general ( self ) -> Ninguno : si es self . _generales : uno mismo . _generales -= 1 def remove_secret_documents ( self ) -> Ninguno : si es self . _documentos_secretos : self . _documentos_secretos -= 1 @property def is_command_ready ( self ) -> bool : return self . _generales > 0 clase ScoutSpy ( Spy ): """ Scout (espía concreto) """ def __init__ ( self ): self . _información_recolectada = {} # Aquí ya conocemos el tipo de objeto específico def visit_military_base ( self , military_base : MilitaryBase ) -> Ninguno : self . _collected_info [ 'base' ] = 'Base militar: \n\t {} \n\t Listo: {} ' . format ( str ( base_militar ), 'Sí' si base_militar . is_combat_ready else 'No' ) def visit_sede ( self , sede : Sede ) -> Ninguno : self . _collected_info [ 'sede' ] = 'Sede: \n\t {} \n\t Comando: {} ' . format ( str ( sede central ), 'En ejecución' si sede central . is_command_ready else 'No operativo' ) def report ( self ) -> str : return 'Información del explorador: \n {} \n ' . formato ( ' \n ' . join ( self . _collected_info . valores ()) ) clase JamesBond ( Espía ): """ James Bond (otro espía específico) """ def visit_military_base ( self , military_base : MilitaryBase ) -> Ninguno : # James Bond visita la base militar military_base . remove_secret_draftings () # roba los dibujos secretos de military_base . remove_nuclear_submarine () # y finalmente explota un submarino nuclear def visita_sede ( self , sede : Sede ) -> Ninguno : #James Bond visita la sede . remove_general () # ... sede . remove_general () # ... sede . remove_secret_documents () # ... sede . remove_general () # Destruye secuencialmente todos los cuarteles generales . remove_secret_documents () # y roba todos los documentos secretos if __name__ == '__main__' : base = MilitaryBase () hq = Cuartel General () # No importa qué instalaciones de MilitaryFacility = [ base , hq ] # type: List[MilitaryFacility] scout = ScoutSpy () print ( 'Enviando un scout... \n ' ) for f en las instalaciones : f . aceptar ( explorar ) imprimir ( scout.informe ( ) ) print ( 'Enviando a Bond a una misión... \n ' ) spy = JamesBond () para f en las instalaciones : f . aceptar ( espiar ) print ( 'Enviando un explorador para actualizar datos... \n ' ) para f en instalaciones : f . aceptar ( explorar ) imprimir ( scout.informe ( ) ) SALIDA """ : Enviando un explorador... Información del explorador: Cuartel general central: Hay 3 generales y 2 documentos secretos en el cuartel general Comando: Base militar en funcionamiento: Hay 1 submarino nuclear y 1 dibujo secreto en la base militar Preparación para el combate: Sí Enviar a Bond a una misión... Enviando un explorador para actualizar los datos... Información del explorador: Cuartel general central: Hay 0 generales y 0 documentos secretos en el cuartel general Comando: No funciona Base militar: Hay 0 submarinos nucleares y 0 dibujos secretos en la base militar Estado de preparación: Ninguno """

Delfos

Ejemplo de implementación en Delphi demostración del programa ; tipo Point2D = clase ; Punto3D = clase ; IVisitor = procedimiento de interfaz Visit ( p : Point2D ) ; sobrecarga ; procedimiento Visita ( p : Point3D ) ; sobrecarga ; fin ; Punto = clase privada FMetric : Doble ; propiedad pública Métrica : Doble lectura FMetric escribir FMetric ; procedimiento Aceptar ( visitante : IVisitor ) ; virtuales ; abstracto ; fin ; Point2D = clase ( Punto ) privado FX : Doble ; AF : Doble ; propiedad pública X : Doble lectura FX ; propiedad Y : Doble lectura FY ; constructor Create ( const x , y : Double ) ; procedimiento Aceptar ( Visitante : IVisitor ) ; anular ; fin ; Point3D = clase ( Punto ) privado FX : Doble ; AF : Doble ; FZ : Doble ; propiedad pública X : Doble lectura FX ; propiedad Y : Doble lectura FY ; propiedad Z : lectura doble FZ ; constructor Create ( const x , y , z : Double ) ; procedimiento Aceptar ( Visitante : IVisitor ) ; anular ; fin ; Euklid = clase ( TInterfacedObject , IVisitor ) visita de procedimiento público ( p : Point2D ) ; sobrecarga ; procedimiento Visita ( p : Point3D ) ; sobrecarga ; fin ; Chebyshev = clase ( TInterfacedObject , IVisitor ) visita de procedimiento público ( p : Point2D ) ; sobrecarga ; procedimiento Visita ( p : Point3D ) ; sobrecarga ; fin ; {Punto2D} procedimiento Point2D . Aceptar ( Visitante : IVisitor ) ; comienza Visitante . Visita ( Auto ) ; fin ; constructor Punto2D . Crear ( const x , y : Doble ) ; comenzar FX := x ; AF := y ; fin ; {Punto3D} procedimiento Point3D . Aceptar ( Visitante : IVisitor ) ; comienza Visitante . Visita ( Auto ) ; fin ; constructor Point3D . Crear ( const x , y , z : Doble ) ; comenzar FX := x ; AF := y ; Efectos := z ; fin ; { Euclides } procedimiento Eulid . Visita ( p : Point2D ) ; comienza pág . Métrica := Sqrt ( Sqr ( p . X ) + Sqr ( p . Y )) ; fin ; procedimiento Eulid . Visita ( p : Point3D ) ; comienza pág . Métrica := Sqrt ( Sqr ( p . X ) + Sqr ( p . Y ) + Sqr ( p . Z )) ; fin ; {Chebyshev} procedimiento Chebyshev . Visita ( p : Point2D ) ; var hacha , ay : Doble ; empezar hacha := Abs ( p . X ) ; ay := Abs ( p . Y ) ; si ax > ay entonces p . Métrica := ax else p . Métrica : = ay fin ; procedimiento Chebyshev . Visita ( p : Point3D ) ; var ax , ay , az , max : Doble ; empezar hacha := Abs ( p . X ) ; ay := Abs ( p . Y ) ; az := Abs ( p . Z ) ; si ax > ay entonces max := ax else max := ay ; si max < az entonces max := az ; pág . Métrica := max ; fin ; varp : Punto ; _ v : IV Visitante ; comenzar p := Point2D . Crear ( 1 , 2 ) ; v := Chebyshev . crear ; pág . aceptar ( v ) ; WriteLn ( p . Métrica : 0 : 2 ) ; v := Eulides . crear ; pág . aceptar ( v ) ; WriteLn ( p . Métrica : 0 : 2 ) ; pág . Gratis ; Leerln ; // esperar a que presione Entrar fin .

Rápido

Ejemplo de implementación en Swift protocol WarehouseItem { var nombre : String { obtener conjunto } var isBroken : Bool { obtener conjunto } var precio : Int { obtener conjunto } } class ElementoAlmacénImpl : ElementoAlmacén { var nombre : String = "" var isBroken : Bool = falso var precio : Int = 0 init ( nombre : String , isBroken : Bool , precio : Int ) { self . nombre = nombre propio . isBroken = isBroken self . precio = precio } } protocol Warehouse { var items : [ WarehouseItem ] { get set } func addItem ( item : WarehouseItem ) func accept ( visitante : BasicVisitor ) } class WarehouseImpl : Warehouse { var items : [ WarehouseItem ] = [] func addItem ( elemento : WarehouseItem ) { elementos . agregar ( elemento ) } func accept ( visitante : BasicVisitor ) { for item in items { visitante . visita ( elemento como AnyObject ) } } } protocolo BasicVisitor { visita func ( _ anObject : AnyObject ) } clase QualityCheckerVisitor : BasicVisitor { func visit ( _ anObject : AnyObject ) { if let obj = anObject as ? ArtículoAlmacén { si obj . isBroken { print ( "es Broken true" ) } else { print ( "is Broken false" ) } si let _ = anObject as ? Almacén { print ( "Buen Almacén" ) } } } } clase PriceCheckerVisitor : BasicVisitor { func visit ( _ anObject : AnyObject ) { if let obj = anObject as ? WarehouseItem { print ( " \( obj . nombre ) | Precio: \( obj . precio ) frotar." ) } si let _ = anObject as ? Almacén { print ( "Costo ninguno" ) } } } // Usar visitante let almacén = WarehouseImpl () almacén _ addItem ( elemento : WarehouseItemImpl ( nombre : "Elemento 1" , isBroken : verdadero , precio : 100 )) almacén . addItem ( artículo : WarehouseItemImpl ( nombre : "Artículo 2" , isBroken : falso , precio : 300 )) almacén . addItem ( elemento : WarehouseItemImpl ( nombre : "Elemento 3" , isBroken : falso , precio : 500 )) let precio = PriceCheckerVisitor () let qulity = QualityCheckerVisitor () almacén _ aceptar ( visitante : precio ) almacén . aceptar ( visitante : calidad )

Literatura

  • E. Gamma, R. Helm, R. Johnson, J. Vlissides . Técnicas de diseño orientado a objetos. Patrones de diseño. - San Petersburgo. : Pedro, 2001. - 368 p. — ISBN 5-272-00355-1 .

Enlaces