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 |
Sí |
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:
- hay varios objetos de diferentes clases con diferentes interfaces, pero sobre ellos se deben realizar operaciones que dependen de clases específicas;
- es necesario realizar varias operaciones en la estructura que complican la estructura;
- a menudo se agregan nuevas operaciones en la estructura.
Ventajas y desventajas
Beneficios :
- simplifica la adición de nuevas operaciones;
- unión de operaciones relacionadas en la clase Visitor;
- la clase Visitorpuede recordar algún estado en sí misma mientras atraviesa el contenedor.
Desventajas :
- es difícil agregar nuevas clases, porque la jerarquía del visitante y sus hijos necesita ser actualizada.
Implementación
- Agregue un método accept(Visitor)a la jerarquía de "elementos".
- Cree una clase base Visitory defina métodos visit()para cada tipo de elemento.
- Cree clases derivadas Visitorpara cada operación realizada en los elementos.
- El cliente crea un objeto Visitory lo pasa al método llamadoaccept().
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 ;
}
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 ); } }
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 .; } }
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 ();
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
"""
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 .
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