stringtranslate.com

Doble despacho

En ingeniería de software , el despacho doble es una forma especial de despacho múltiple y un mecanismo que envía una llamada de función a diferentes funciones concretas dependiendo de los tipos de tiempo de ejecución de dos objetos involucrados en la llamada. En la mayoría de los sistemas orientados a objetos , la función concreta que se llama desde una llamada de función en el código depende del tipo dinámico de un único objeto y, por lo tanto, se conocen como llamadas de despacho único o simplemente llamadas de función virtual .

Dan Ingalls describió por primera vez cómo utilizar el despacho doble en Smalltalk , llamándolo polimorfismo múltiple . [1]

Descripción general

El problema general abordado es cómo enviar un mensaje a diferentes métodos dependiendo no sólo del receptor sino también de los argumentos.

Para ello, sistemas como CLOS implementan despacho múltiple . El envío doble es otra solución que reduce gradualmente el polimorfismo en sistemas que no admiten el envío múltiple.

Casos de uso

El doble despacho es útil en situaciones donde la elección del cálculo depende de los tipos de tiempo de ejecución de sus argumentos. Por ejemplo, un programador podría utilizar el doble despacho en las siguientes situaciones:

Un modismo común

El lenguaje común, como en los ejemplos presentados anteriormente, es que la selección del algoritmo apropiado se basa en los tipos de argumentos de la llamada en tiempo de ejecución. Por lo tanto, la llamada está sujeta a todos los costos de rendimiento adicionales habituales asociados con la resolución dinámica de llamadas, generalmente más que en un lenguaje que solo admite el envío de un solo método. En C++ , por ejemplo, una llamada a una función dinámica generalmente se resuelve mediante un único cálculo de compensación , lo cual es posible porque el compilador conoce la ubicación de la función en la tabla de métodos del objeto y, por lo tanto, puede calcular estáticamente la compensación. En un lenguaje que soporta doble despacho, esto es un poco más costoso, porque el compilador debe generar código para calcular el desplazamiento del método en la tabla de métodos en tiempo de ejecución, aumentando así la longitud total de la ruta de instrucción (en una cantidad que probablemente no sea mayor que el número total de llamadas a la función, que puede no ser muy significativo).

Ejemplo en Rubí

Un caso de uso común es mostrar un objeto en un puerto de visualización que puede ser una pantalla o una impresora, o algo completamente diferente que aún no existe. Esta es una implementación ingenua de cómo lidiar con esos diferentes medios.

clase Rectángulo def display_on ( puerto ) # selecciona el código correcto según la clase de objeto caso puerto cuando DisplayPort # código para mostrar en DisplayPort cuando PrinterPort # código para mostrar en PrinterPort cuando RemotePort # código para mostrar en RemotePort end end end                 

Se necesitaría escribir lo mismo para Oval, Triangle y cualquier otro objeto que quiera mostrarse en un medio, y todo debería reescribirse si se creara un nuevo tipo de puerto. El problema es que existe más de un grado de polimorfismo: uno para enviar el método display_on a un objeto y otro para seleccionar el código (o método) correcto para mostrar.

Una solución mucho más limpia y fácil de mantener es realizar un segundo envío, esta vez para seleccionar el método correcto para mostrar el objeto en el medio:

clase Rectángulo def display_on ( puerto ) # segundo puerto de despacho . display_rectangle ( self ) fin fin      clase Oval def display_on ( puerto ) # segundo puerto de despacho . display_oval ( self ) fin fin      clase DisplayPort def display_rectangle ( objeto ) # código para mostrar un rectángulo en un extremo DisplayPort def display_oval ( objeto ) # código para mostrar un óvalo en un extremo DisplayPort # ... fin          clase PrinterPort def display_rectangle ( objeto ) # código para mostrar un rectángulo en un PrinterPort end def display_oval ( objeto ) # código para mostrar un óvalo en un PrinterPort end #... end          

Doble despacho en C++

A primera vista, el doble despacho parece ser un resultado natural de la sobrecarga de funciones . La sobrecarga de funciones permite que la función llamada dependa del tipo de argumento. Sin embargo, la sobrecarga de funciones se realiza en tiempo de compilación mediante " name mangling ", donde el nombre interno de la función codifica el tipo de argumento. Por ejemplo, una función foo(int)puede llamarse internamente __foo_i y la función foo(double)puede llamarse __foo_d . Por lo tanto, no hay colisión de nombres ni búsqueda de tablas virtuales. Por el contrario, el despacho dinámico se basa en el tipo de objeto que llama, lo que significa que utiliza funciones virtuales (anulación) en lugar de sobrecarga de funciones , y da como resultado una búsqueda de vtable. Considere el siguiente ejemplo, escrito en C++ , de colisiones en un juego:

clase nave espacial {}; clase ApolloSpacecraft : nave espacial pública {};       class Asteroide { public : virtual void CollideWith ( SpaceShip & ) { std :: cout << "El asteroide chocó contra una nave espacial \n " ; } virtual void CollideWith ( ApolloSpacecraft & ) { std :: cout << "El asteroide golpeó una ApolloSpacecraft \n " ; } };                  clase ExplodingAsteroid : public Asteroid { public : void CollideWith ( SpaceShip & ) override { std :: cout << "ExplodingAsteroide golpeó una nave espacial \n " ; } void CollideWith ( ApolloSpacecraft & ) override { std :: cout << "Explosión de asteroide golpeó una ApolloSpacecraft \n " ; } };                     

Si usted tiene:

Asteroide el Asteroide ; Nave espacial la nave espacial ; Nave espacial Apollo la nave espacial Apollo ;   

entonces, debido a la sobrecarga de funciones,

el Asteroide . Chocar con ( la nave espacial ); el Asteroide . Chocar con ( la nave espacial Apollo ); 

imprimirán, respectivamente, Asteroid hit a SpaceShipy Asteroid hit an ApolloSpacecraft, sin utilizar ningún envío dinámico. Además:

ExplodingAsteroide el ExplodingAsteroide ; el asteroide explosivo . Chocar con ( la nave espacial ); el asteroide explosivo . Chocar con ( la nave espacial Apollo );  

se imprimirá ExplodingAsteroid hit a SpaceShipy ExplodingAsteroid hit an ApolloSpacecraftrespectivamente, nuevamente sin envío dinámico.

Con una referencia a un Asteroid, se utiliza el despacho dinámico y este código:

Asteroide y theAsteroidReference = theExplodingAsteroid ; la referencia de asteroides . Chocar con ( la nave espacial ); la referencia de asteroides . Chocar con ( la nave espacial Apollo );    

imprime ExplodingAsteroid hit a SpaceShipy ExplodingAsteroid hit an ApolloSpacecraft, nuevamente como se esperaba. Sin embargo, el siguiente código no funciona como se desea:

Nave espacial y theSpaceShipReference = theApolloSpacecraft ; el Asteroide . ChocarCon ( theSpaceShipReference ); la referencia de asteroides . ChocarCon ( theSpaceShipReference );   

El comportamiento deseado es vincular estas llamadas a la función que toma theApolloSpacecraftcomo argumento, ya que ese es el tipo instanciado de la variable, lo que significa que la salida esperada sería Asteroid hit an ApolloSpacecrafty ExplodingAsteroid hit an ApolloSpacecraft. Sin embargo, el resultado es en realidad Asteroid hit a SpaceShipy ExplodingAsteroid hit a SpaceShip. El problema es que, mientras que las funciones virtuales se distribuyen dinámicamente en C++, la sobrecarga de funciones se realiza de forma estática.

El problema descrito anteriormente se puede resolver simulando un envío doble, por ejemplo utilizando un patrón de visitante . Supongamos que el código existente se extiende de modo que a ambos SpaceShipy ApolloSpacecraftse les dé la función

vacío virtual CollideWith ( Asteroide y enAsteroide ) { enAsteroide . Chocar con ( * esto ); }     

Luego, aunque el ejemplo anterior todavía no funciona correctamente, replantear las llamadas para que la nave espacial sea el agente nos da el comportamiento deseado:

Nave espacial y theSpaceShipReference = theApolloSpacecraft ; Asteroide y theAsteroidReference = theExplodingAsteroid ; la referencia de la nave espacial . Chocar con ( el asteroide ); la referencia de la nave espacial . ChocarCon ( laReferenciaAsteroides );      

Se imprime Asteroid hit an ApolloSpacecrafty ExplodingAsteroid hit an ApolloSpacecraft, como se esperaba. La clave es que theSpaceShipReference.CollideWith(theAsteroidReference);hace lo siguiente en tiempo de ejecución:

  1. theSpaceShipReferencees una referencia, por lo que C++ busca el método correcto en vtable. En este caso, llamará ApolloSpacecraft::CollideWith(Asteroid&).
  2. Dentro hay una referencia, por lo que ApolloSpacecraft::CollideWith(Asteroid&)dará como resultado otra búsqueda de vtable . En este caso, es una referencia a un así se llamará.inAsteroidinAsteroid.CollideWith(*this)inAsteroidExplodingAsteroidExplodingAsteroid::CollideWith(ApolloSpacecraft&)

Doble despacho en C#

En C# , cuando se llama a un método de instancia que acepta un argumento, se puede lograr el envío múltiple sin emplear el patrón de visitante. Esto se hace utilizando el polimorfismo tradicional y al mismo tiempo convirtiendo el argumento en dinámico . [3] El enlazador en tiempo de ejecución elegirá el método de sobrecarga apropiado en tiempo de ejecución. Esta decisión tendrá en cuenta el tipo de tiempo de ejecución de la instancia del objeto (polimorfismo), así como el tipo de tiempo de ejecución del argumento.

Doble despacho en Eiffel

El lenguaje de programación Eiffel puede aplicar el concepto de agentes al problema del doble despacho. El siguiente ejemplo aplica la construcción del lenguaje del agente al problema de doble envío.

Considere un dominio de problema con varias formas de FORMA y de superficie de dibujo sobre la cual dibujar una FORMA. Tanto SHAPE como SURFACE conocen una función llamada "dibujar" en sí mismos, pero no entre sí. Queremos que los objetos de los dos tipos interactúen de forma covariante entre sí en un envío doble utilizando un patrón de visitante.

El desafío es conseguir que una SUPERFICIE polimórfica dibuje una FORMA polimórfica sobre sí misma.

Producción

El siguiente ejemplo de salida muestra los resultados de dos objetos visitantes de SUPERFICIE que se pasan polimórficamente sobre una lista de objetos SHAPE polimórficos. El patrón de código de visitante sólo reconoce FORMA y SUPERFICIE de forma genérica y no el tipo específico de ninguna de ellas. En cambio, el código se basa en el polimorfismo en tiempo de ejecución y la mecánica de los agentes para lograr una relación covariante altamente flexible entre estas dos clases diferidas y sus descendientes.

dibuja un POLÍGONO rojo en ETCHASKETCHdibuja un POLÍGONO rojo en GRAFFITI_WALLdibuja un RECTÁNGULO gris en ETCHASKETCHdibuja un RECTÁNGULO gris en GRAFFITI_WALLdibuja un CUADRILATERAL verde en ETCHASKETCHdibuja un CUADRILATERAL verde en GRAFFITI_WALLdibuja un PARALELOGRAMA azul en ETCHASKETCHdibuja un PARALELOGRAMO azul en GRAFFITI_WALLdibuja un POLÍGONO amarillo en ETCHASKETCHdibuja un POLÍGONO amarillo en GRAFFITI_WALLdibuja un RECTÁNGULO morado en ETCHASKETCHdibuja un RECTÁNGULO morado en GRAFFITI_WALL

Configuración

Antes de mirar FORMA o SUPERFICIE, debemos examinar el uso desacoplado de alto nivel de nuestro doble despacho.

Patrón de visitante

El patrón de visitante funciona mediante un objeto visitante que visita los elementos de una estructura de datos (por ejemplo, lista, árbol, etc.) polimórficamente, aplicando alguna acción (llamada o agente) contra los objetos elemento polimórficos en la estructura objetivo visitada.

En nuestro ejemplo a continuación, hacemos una lista de objetos FORMA polimórficos, visitando cada uno de ellos con una SUPERFICIE polimórfica, solicitando que se dibuje la FORMA en la SUPERFICIE.

hacer- Imprimir formas en superficies.locall_shapes : ARRAYED_LIST [ FORMA ]  l_superficies : ARRAYED_LIST [ SUPERFICIE ]  hacercrear l_shapes . hacer ( 6 )  l_formas . extender ( crear { POLÍGONO }. make_with_color ( "rojo" ))   l_formas . extender ( crear { RECTÁNGULO }. make_with_color ( "gris" ))   l_formas . extender ( crear { CUADRILATERAL }. make_with_color ( "verde" ))   l_formas . extender ( crear { PARALELOGRAMO }. make_with_color ( "azul" ))   l_formas . extender ( crear { POLÍGONO }. make_with_color ( "amarillo" ))   l_formas . extender ( crear { RECTÁNGULO }. make_with_color ( "púrpura" ))   crear l_surfaces . hacer ( 2 )  l_superficies . extender ( crear { ETCHASKETCH }. hacer )  l_superficies . extender ( crear { GRAFFITI_WALL }. crear )  a través de l_shapes como bucle ic_shapes    a través de l_surfaces como bucle ic_surfaces    ic_superficies . artículo . agente_dibujo ( ic_shapes . item . agente_datos_dibujo ) finfinfin

Comenzamos creando una colección de objetos FORMA y SUPERFICIE. Luego iteramos sobre una de las listas (FORMA), permitiendo que los elementos de la otra (SUPERFICIE) visiten cada una de ellas por turno. En el código de ejemplo anterior, los objetos SURFACE visitan objetos SHAPE.

El código realiza una llamada polimórfica en {SURFACE}.draw indirectamente a través del `drawing_agent', que es la primera llamada (despacho) del patrón de doble envío. Pasa un agente indirecto y polimórfico (`drawing_data_agent'), permitiendo que nuestro código de visitante solo sepa dos cosas:

Debido a que tanto SURFACE como SHAPE definen sus propios agentes, nuestro código de visitante se libera de tener que saber cuál es la llamada apropiada a realizar, polimórficamente o de otra manera. Este nivel de direccionamiento indirecto y desacoplamiento simplemente no se puede lograr en otros lenguajes comunes como C, C++ y Java, excepto mediante alguna forma de reflexión o sobrecarga de funciones con coincidencia de firmas.

SUPERFICIE

Dentro de la llamada polimórfica a {SURFACE}.draw está la llamada a un agente, que se convierte en la segunda llamada polimórfica o despacho en el patrón de doble despacho.

clase diferida SUPERFICIEcaracterística { NONE } - Inicialización  hacer-- Inicializar corriente.haceragente_dibujo := sorteo de agente   fincaracterística - Acceso agente_dibujo : PROCEDIMIENTO [ CUALQUIER , TUPLE [ CADENA , CADENA ]]     -- Agente dibujante de corriente.característica { NONE } - Implementación  dibujar ( a_data_agent : FUNCIÓN [ CUALQUIERA , TUPLE , TUPLE [ nombre , color : CADENA ]] )        -- Dibujar `a_shape' en Current.locall_resultado : TUPLE [ nombre , color : CADENA ]    hacerl_resultado := a_data_agent ( Nulo )   imprimir ( "dibujar un " + l_resultado . color + " " + l_resultado . nombre + " on " + tipo + "%N" )             fintipo : CUERDA -- Escriba el nombre de la corriente.final diferido fin

El argumento del agente en la línea 19 y la llamada en la línea 24 son polimórficos y están desacoplados. El agente está desacoplado porque la función {SURFACE}.draw no tiene idea de en qué clase se basa "a_data_agent". No hay forma de saber de qué clase se derivó el agente de operación, por lo que no tiene que provenir de SHAPE o de uno de sus descendientes. Esta es una clara ventaja de los agentes Eiffel sobre la herencia única, la vinculación dinámica y polimórfica de otros lenguajes.

El agente es dinámicamente polimórfico en tiempo de ejecución porque el objeto se crea en el momento en que se necesita, dinámicamente, donde se determina la versión de la rutina objetivada en ese momento. El único conocimiento fuertemente vinculado es el tipo Resultado de la firma del agente, es decir, una TUPLE denominada con dos elementos. Sin embargo, este requisito específico se basa en una demanda de la característica envolvente (por ejemplo, la línea #25 usa los elementos nombrados de TUPLE para cumplir con la característica "dibujar" de SURFACE), lo cual es necesario y no se ha evitado (y tal vez no pueda serlo). .

Finalmente, observe cómo solo la función `drawing_agent' se exporta a CUALQUIER cliente. Esto significa que el código del patrón de visitante (que es el ÚNICO cliente de esta clase) sólo necesita conocer al agente para realizar su trabajo (por ejemplo, usar el agente como característica aplicada a los objetos visitados).

FORMA

La clase SHAPE tiene la base (por ejemplo, datos de dibujo) para lo que se dibuja, tal vez en una SUPERFICIE, pero no tiene por qué ser así. Nuevamente, los agentes proporcionan la dirección indirecta y los agnósticos de clase necesarios para que la relación covariante con SHAPE esté lo más desacoplada posible.

Además, tenga en cuenta el hecho de que SHAPE solo proporciona `drawing_data_agent' como una característica totalmente exportada a cualquier cliente. Por lo tanto, la única forma de interactuar con SHAPE, además de la creación, es a través de las funciones del `drawing_data_agent', que es utilizado por CUALQUIER cliente para recopilar indirecta y polimórficamente datos de dibujo para SHAPE.

clase diferida FORMAcaracterística { NONE } - Inicialización  make_with_color ( a_color : color similar )   -- Hacer con `a_color' como `color'.hacercolor := un_color  agente_datos_dibujo := agente datos_dibujo   asegurarconjunto_color : color . misma_cadena ( un_color )  fincaracterística - Acceso agente_datos_dibujo : FUNCIÓN [ CUALQUIERA , TUPLE , como datos_dibujo ]     -- Agente de datos para dibujo.característica { NONE } - Implementación  datos_dibujo : TUPLE [ nombre : como nombre ; color : como color ]       - Datos necesarios para el dibujo de corriente.hacerResultado := [ nombre , color ]   finnombre : CUERDA -- Nombre del objeto actual.final diferido color : CUERDA -- Color de la corriente.fin

Ejemplo de nave espacial clásica

Una variación del ejemplo clásico de la nave espacial tiene uno o más objetos de nave espacial deambulando por un universo lleno de otros elementos como asteroides rebeldes y estaciones espaciales. Lo que queremos es un método de doble envío para manejar encuentros (por ejemplo, posibles colisiones) entre dos objetos covariantes en nuestro universo imaginario. En nuestro ejemplo a continuación, la excursión de salida de nuestro USS Enterprise y USS Excelsior será:

Starship Enterprise cambia de posición de A-001 a A-002.¡Starship Enterprise toma medidas evasivas, evitando el asteroide "Rogue 1"!Starship Enterprise cambia de posición de A-002 a A-003.¡Starship Enterprise toma medidas evasivas, evitando el asteroide "Rogue 2"!¡Starship Enterprise envía un equipo científico a Starship Excelsior cuando pasan!Starship Enterprise cambia de posición de A-003 a A-004.Starship Excelsior cambia de posición de A-003 a A-005.¡Starship Enterprise toma medidas evasivas, evitando el asteroide "Rogue 3"!Starship Excelsior está cerca de la estación espacial Deep Space 9 y es acoplable.Starship Enterprise cambia de posición de A-004 a A-005.¡Starship Enterprise envía un equipo científico a Starship Excelsior cuando pasan!Starship Enterprise está cerca de la estación espacial Deep Space 9 y es acoplable.

Visitante

El visitante del ejemplo clásico de la nave espacial también tiene un mecanismo de doble envío.

hacer-- Permitir que los objetos de la NAVE ESPACIAL visiten y se muevan en un universo.locall_universo : ARRAYED_LIST [ ESPACIO_OBJECT ]  l_empresa ,l_excelsior : NAVE ESPACIAL hacercrear l_enterprise . make_with_name ( "Empresa" , "A-001" )   crear l_excelsior . make_with_name ( "Excelsior" , "A-003" )   crear l_universe . hacer ( 0 )  l_universo . fuerza ( l_empresa ) l_universo . fuerza ( crear { ASTEROID }. make_with_name ( "Rogue 1" , "A-002" ))    l_universo . fuerza ( crear { ASTEROID }. make_with_name ( "Rogue 2" , "A-003" ))    l_universo . fuerza ( l_excelsior ) l_universo . fuerza ( crear { ASTEROID }. make_with_name ( "Rogue 3" , "A-004" ))    l_universo . fuerza ( crear { SPACESTATION }. make_with_name ( "Deep Space 9" , "A-005" ))    visitar ( l_enterprise , l_universe )  l_empresa . establecer_posicion ( "A-002" ) visitar ( l_enterprise , l_universe )  l_empresa . establecer_posicion ( "A-003" ) visitar ( l_enterprise , l_universe )  l_empresa . establecer_posicion ( "A-004" ) l_excelsior . establecer_posicion ( "A-005" ) visitar ( l_enterprise , l_universe )  visitar ( l_excelsior , l_universe )  l_empresa . establecer_posicion ( "A-005" ) visitar ( l_enterprise , l_universe )  fincaracterística { NONE } - Implementación  visitar ( un_objeto : ESPACIO_OBJECT ; un_universo : ARRAYED_LIST [ ESPACIO_OBJECT ] )     -- `a_object' visita `a_universe'.hacera través de a_universe como bucle ic_universe    marque adjunto { SPACE_OBJECT } ic_universe . elemento como al_universe_object entonces      un_objeto . agente_encuentro . llamar ( [ al_universe_object . sensor_data_agent ] ) finfinfin

El doble despacho se puede ver en la línea 35, donde dos agentes indirectos están trabajando juntos para proporcionar dos llamadas covariantes que funcionan en perfecto concierto polimórfico entre sí. El `a_object' de la función `visit' tiene un `encounter_agent' que se llama con los datos del sensor del `sensor_data_agent' provenientes del `al_universe_object'. La otra parte interesante de este ejemplo en particular es la clase SPACE_OBJECT y su característica "encuentro":

Acción del visitante

Las únicas características exportadas de un SPACE_OBJECT son los agentes para los datos del encuentro y del sensor, así como la capacidad de establecer una nueva posición. Cuando un objeto (la nave espacial) visita cada objeto en el universo, los datos del sensor se recopilan y se pasan al objeto visitante en su agente de encuentro. Allí, los datos del sensor de sensor_data_agent (es decir, los elementos de datos de la TUPLE sensor_data devueltos por la consulta sensor_data_agent) se evalúan con respecto al objeto actual y se toma un curso de acción basado en esa evaluación (consulte `encuentro' en SPACE_OBJECT a continuación). Todos los demás datos se exportan a {NONE}. Esto es similar a los ámbitos C, C++ y Java de Private. Como características no exportadas, los datos y las rutinas se utilizan solo internamente por cada SPACE_OBJECT. Finalmente, tenga en cuenta que las llamadas de encuentro a "imprimir" no incluyen información específica sobre posibles clases descendientes de SPACE_OBJECT. Lo único que se encuentra en este nivel en la herencia son aspectos relacionales generales basados ​​completamente en lo que se puede conocer a partir de los atributos y rutinas de un SPACE_OBJECT general. El hecho de que el resultado de la "impresión" tenga sentido para nosotros, como seres humanos, basándose en lo que sabemos o imaginamos sobre las naves espaciales, las estaciones espaciales y los asteroides es simplemente una planificación lógica o una coincidencia. El SPACE_OBJECT no está programado con ningún conocimiento específico de sus descendientes.

clase diferida ESPACIO_OBJETOcaracterística { NONE } - Inicialización  make_with_name ( a_name : como nombre ; a_position : como posición )       -- Inicialice Current con `a_name' y `a_position'. hacer nombre := un_nombre   posición := una_posición   sensor_data_agent := agente sensor_data    encuentro_agente := encuentro con el agente    asegurar conjunto_nombre : nombre . misma_cadena ( un_nombre )   position_set : posición . misma_cadena ( una_posición )   fincaracterística - Acceso agente_encuentro : PROCEDIMIENTO [ CUALQUIER , TUPLE ]    -- Agente para gestionar encuentros con Current.sensor_data_agent : FUNCIÓN [ CUALQUIERA , TUPLE , adjunto como sensor_data_anchor ]       -- Agente para devolver datos del sensor de corriente.característica - Configuración set_position ( a_position : posición similar )    -- Establecer `posición' con `a_position'. hacer print ( escriba + " " + nombre + " cambia la posición de " + posición + " a " + a_position + ".%N" )                posición := una_posición   asegurar position_set : posición . misma_cadena ( una_posición )   fincaracterística { NONE } - Implementación  encuentro ( a_sensor_agent : FUNCIÓN [ CUALQUIER , TUPLE , adjunto como sensor_data_anchor ] )        -- Detectar e informar sobre el estado de colisión de Current con `a_radar_agent'. hacer a_sensor_agent . llamar ( [ nulo ] )  marque adjunto { como sensor_data_anchor } a_sensor_agent . last_result como al_sensor_data entonces        si no nombre . misma_cadena ( al_sensor_data . nombre ) entonces     si ( posición . misma_cadena ( al_sensor_data . posición )) entonces    si (( al_sensor_data . is_dockable y is_dockable ) y     ( is_manned y al_sensor_data . is_manned ) y    ( is_manueverable y al_sensor_data . is_not_manueverable )) entonces    print ( escriba + " " + nombre + " está cerca de " + al_sensor_data . escriba + " " +             al_sensor_data . nombre + "y es acoplable.%N" )   elseif (( is_dockable y al_sensor_data . is_dockable ) y     ( is_manned y al_sensor_data . is_manned ) y    ( is_manueverable y al_sensor_data . is_manueverable )) entonces    print ( escriba + " " + nombre + " envía un equipo científico a " + al_sensor_data . escriba + " " +             al_sensor_data . nombre + " ¡mientras pasan!%N" )   elseif ( is_manned y al_sensor_data . is_not_manned ) entonces     print ( escriba + " " + nombre + " realiza una acción evasiva, evitando " +         al_sensor_data . escriba + "`" + al_sensor_data . nombre + "'!%N" )       fin fin fin fin finnombre : CUERDA  -- Nombre de la Actual.tipo : CUERDA  -- Tipo de Corriente. diferido finposición : CUERDA  -- Posición de Actual.is_dockable : BOOLEAN  -- ¿Se puede acoplar Current a otro objeto tripulado? diferido finis_manned : BOOLEAN  -- ¿Es Current un objeto tripulado? diferido finis_manueverable : BOOLEAN  -- ¿Se puede mover la corriente? diferido finsensor_data : adjunto como sensor_data_anchor    -- Datos del sensor de corriente. hacer Resultado : = [ nombre , tipo , posición , is_dockable , no is_dockable , is_manned , no is_manned , is_manueverable , no is_manueverable ]              fin sensor_data_anchor : TUPLE desmontable [ nombre , tipo , posición : STRING ; is_dockable , is_not_dockable , is_manned , is_not_manned , is_manueverable , is_not_manueverable : BOOLEAN ]              -- Ancla del tipo de datos del sensor de corriente.fin

Hay tres clases descendientes de SPACE_OBJECT:

SPACE_OBJECT ESTACIÓN ESPACIAL DE LA NAVE ESPACIAL ASTEROIDE

En nuestro ejemplo, la clase ASTEROID se usa para los elementos "Rogue", SPACESHIP para las dos naves estelares y SPACESTATION para Deep Space Nine. En cada clase, la única especialización es la configuración de la característica "tipo" y de ciertas propiedades del objeto. El "nombre" se proporciona en la rutina de creación así como la "posición". Por ejemplo: A continuación se muestra el ejemplo de NAVE ESPACIAL.

claseASTRONAVEheredarESPACIO_OBJETOcrearhacer_con_nombrecaracterística { NONE } - Implementación  tipo : STRING = "Nave estelar"    -- <Precursores>is_dockable : BOOLEAN = Verdadero    -- <Precursores>is_manned : BOOLEAN = Verdadero    -- <Precursores>is_manueverable : BOOLEAN = Verdadero    -- <Precursores>fin

Entonces, cualquier NAVE ESPACIAL en nuestro universo es acoplable, tripulada y maniobrable. Otros objetos, como los asteroides, no son ninguna de estas cosas. Una ESTACIÓN ESPACIAL, por otro lado, puede atracar y estar tripulada, pero no es maniobrable. Por lo tanto, cuando un objeto se encuentra con otro, primero verifica si las posiciones los colocan cerca uno del otro y, si es así, luego los objetos interactúan en función de sus propiedades básicas. Tenga en cuenta que los objetos con el mismo tipo y nombre se consideran el mismo objeto, por lo que lógicamente no se permite una interacción.

Conclusión del ejemplo de Eiffel

Con respecto al doble envío, Eiffel permite al diseñador y programador eliminar aún más un nivel de conocimiento directo de objeto a objeto al desacoplar las rutinas de clase de sus clases al convertirlas en agentes y luego pasar esos agentes en lugar de convertirlas en características de objeto directo. llamadas. Los agentes también tienen firmas específicas y posibles resultados (en el caso de consultas), lo que los convierte en vehículos de verificación de tipo estático ideales sin renunciar a detalles específicos del objeto. Los agentes son totalmente polimórficos, de modo que el código resultante sólo tiene el conocimiento específico necesario para realizar su trabajo local. De lo contrario, no se agrega ninguna carga de mantenimiento al tener conocimiento de características de clase internas específicas repartidas entre muchos objetos covariantes. El uso y la mecánica de los agentes lo garantizan. Una posible desventaja del uso de agentes es que un agente es computacionalmente más caro que su contraparte de llamada directa. Teniendo esto en cuenta, nunca se debe dar por sentado el uso de agentes en el doble despacho y su aplicación en los patrones de visitantes. Si se puede ver claramente un límite de diseño en cuanto al dominio de los tipos de clases que estarán involucrados en las interacciones covariantes, entonces una llamada directa es la solución más eficiente en términos de gasto computacional. Sin embargo, si se espera que el dominio de clase de los tipos participantes crezca o difiera sustancialmente, entonces los agentes presentan una excelente solución para reducir la carga de mantenimiento en el patrón de doble despacho.

Ver también

Referencias

  1. ^ Una técnica sencilla para manejar polimorfismo múltiple. En Actas de OOPSLA '86, Sistemas, lenguajes y aplicaciones de programación orientada a objetos, páginas 347–349, noviembre de 1986. Impreso como Avisos SIGPLAN, 21(11). ISBN  0-89791-204-7
  2. ^ C++ más eficaz por Scott Meyers (Addison-Wesley, 1996)
  3. ^ "Uso de Type Dynamic (Guía de programación de C#)". Red de desarrolladores de Microsoft . Microsoft. 30 de septiembre de 2009 . Consultado el 25 de mayo de 2016 . ... La resolución de sobrecarga se produce en tiempo de ejecución en lugar de en tiempo de compilación si uno o más de los argumentos en una llamada a un método tienen el tipo dinámico...