¿Qué pasa el mensaje en OO?

35

He estado estudiando programación OO, principalmente en C ++, C # y Java. Pensé que lo entendía bien con mi comprensión de la encapsulación, la herencia y el polimorfismo (además de leer muchas preguntas en este sitio).

Una cosa que parece aparecer aquí y allá es el concepto de "pasar mensajes". Aparentemente, esto es algo que no se usa durante la programación OO en los idiomas principales de hoy en día, pero es compatible con Smalltalk.

Mis preguntas son:

  • ¿Qué es el mensaje que pasa? (¿Alguien puede dar un ejemplo práctico?)
  • ¿Hay algún soporte para este "paso de mensajes" en C ++, C # o Java?
Tom
fuente
44
Respondí esta pregunta en SO hace un tiempo: stackoverflow.com/a/3104741/10259
Frank Shearar
1
¿Leíste el artículo de Wikipedia ?
Yannis
44
En mi humilde opinión, Objective-C calificaría como un lenguaje convencional. Al menos tanto como C #.
Mouviciel
Estoy de acuerdo con eso, estaba declarando los idiomas "principales" desde mi experiencia
Tom
Una llamada de función miembro es una implementación de pasar un mensaje. El mensaje pasado se identifica por el nombre de la función e incluye la información de los parámetros. El enlace tardío permite que la clase receptora maneje el mismo mensaje de manera diferente a otras clases. No es lo que pretendían los creadores de Simula, y muchas personas se opondrían a llamarlo paso de mensajes y afirmarían (con buena razón) que hacer pasar mensajes es una cosa clave que hace diferente a Simula, pero las llamadas a funciones de miembros siguen siendo esencialmente lo mismo trabajo.
Steve314

Respuestas:

60

¿Qué es el mensaje que pasa? (¿Alguien puede dar un ejemplo práctico?)

La transmisión de mensajes simplemente significa que (en un nivel muy abstracto) el mecanismo fundamental de la ejecución del programa son los objetos que se envían mensajes entre sí. El punto importante es que el nombre y la estructura de estos mensajes no están necesariamente fijos de antemano en el código fuente y pueden ser información adicional. Esta es una parte importante de lo que Alan Kay imaginó originalmente como "programación orientada a objetos".

¿Hay algún soporte para este "paso de mensajes" en C ++, C # o Java?

Este lenguaje implementa una versión limitada del mensaje que pasa a través de llamadas a métodos. Limitado porque el conjunto de mensajes que se pueden enviar está limitado a los métodos declarados en una clase. La ventaja de este enfoque es que se puede implementar de manera muy eficiente y permite un análisis de código estático muy detallado (lo que resulta en todo tipo de beneficios útiles, como la finalización del código).

Por el contrario, los lenguajes que implican el paso de mensajes "reales" a menudo también tienen definiciones de métodos, como una forma conveniente de implementar manejadores de mensajes, pero permiten que las clases implementen manejadores de mensajes más flexibles que permiten al objeto recibir "llamadas a métodos" con nombres arbitrarios (no fijo en tiempo de compilación).

Un ejemplo en Groovy que demuestra el poder de este concepto:

def xml = new MarkupBuilder(writer)
xml.records() {
  car(name:'HSV Maloo', make:'Holden', year:2006) {
    country('Australia')
    record(type:'speed', 'Production Pickup Truck with speed of 271kph')
  }
}

producirá este XML:

<records>
  <car name='HSV Maloo' make='Holden' year='2006'>
    <country>Australia</country>
    <record type='speed'>Production Pickup Truck with speed of 271kph</record>
  </car>
</records>

Tenga en cuenta que records, car, countryy recordson sintácticamente las llamadas de método, pero no existen métodos de ese nombre definido en MarkupBuilder. En cambio, tiene un controlador de mensajes general que acepta todos los mensajes e interpreta los nombres de los mensajes como el nombre de un elemento XML, los parámetros como atributos y los cierres como elementos secundarios.

Michael Borgwardt
fuente
+1 directamente al punto de respuesta. Aceptado para el ejemplo de código. Gracias por su ayuda :)
Tom
Entonces, ¿no podría implementarse simplemente con un lenguaje simple sendMessage(property_name, Array of arguments)y getMessage(property_name, Array of arguments)estático?
Pacerier
1
@Pacerier: claro, pero eso combina las desventajas de ambos enfoques: pierdes la seguridad de los tipos y aún tienes "sendMessage" contaminando tu código en todas partes, por lo que no obtienes la sintaxis elegante.
Michael Borgwardt
¿Sería más correcto decir que, en el ejemplo de Groovy, el manejador de mensajes está recibiendo un mensaje, en lugar de una llamada al método? Inicialmente pones comillas alrededor de la frase "llamada al método", pero en tu última oración, dices que "acepta todos los métodos ", en lugar de "mensajes".
Adam Zerner
@ AdamZerner: tienes razón, lo arreglé.
Michael Borgwardt
28

El paso de mensajes es una forma diferente de manejar la necesidad en el código OO de que un objeto obtenga otro objeto (o potencialmente él mismo) para hacer algo.

En la mayoría de los lenguajes modernos que descienden del enfoque de C ++, lo hacemos con llamadas a métodos. En este caso, el objeto llamado (a través de su definición de clase) pone una gran lista de las llamadas a los métodos que acepta y luego el codificador del objeto que llama simplemente escribe la llamada:

public void doSomething ( String input )
...
other_object.dosomething ( local )

Para lenguajes tipados estáticamente, el compilador puede verificar el tipo de cosa que se llama y confirmar que el método ha sido declarado. Para lenguajes escritos dinámicamente, eso se lleva a cabo en tiempo de ejecución.

Pero, en esencia, lo que sucede es que se envía un paquete de variables a un bloque de código específico.

Paso de mensajes

En los lenguajes de paso de mensajes (como el Objetivo C) en lugar de los métodos hay receptores, pero en general el enfoque de definirlos y llamarlos es muy similar: la diferencia es la forma en que se maneja.

En un lenguaje de mensaje pasado, el compilador puede verificar que el receptor al que ha llamado existe, pero en el peor de los casos, aparecerá una advertencia para decir que no está seguro de que esté allí. Esto se debe a que en el tiempo de ejecución lo que sucederá es que se llamará un bloque de código en el objeto receptor pasando tanto el paquete de variables como la firma del receptor al que desea llamar. Ese bloque de código luego busca el receptor y lo llama. Sin embargo, si el receptor no existe, el código simplemente devolverá un valor predeterminado.

Como resultado, una de las rarezas encontradas al pasar de C ++ / Java -> Objetivo C es comprender que puede "llamar a un método" en un objeto que no fue declarado en el tipo de tiempo de compilación, y que ni siquiera existía en el tipo de tiempo de ejecución ... y que la llamada no generaría una excepción sino que, de hecho, se devolvería un resultado.

Las ventajas de este enfoque son que aplana la jerarquía de la subclase y evita la mayoría de las necesidades de interfaces / herencia múltiple / tipos de pato. También permite que los objetos definan el comportamiento predeterminado cuando se les pide que hagan algo para lo que no tienen un receptor (comúnmente "si no lo hago, reenvío la solicitud a este otro objeto"). También puede simplificar la vinculación a las devoluciones de llamada (por ejemplo, para elementos de la interfaz de usuario y eventos cronometrados) particularmente sobre lenguajes tipados estáticamente como Java (para que pueda hacer que el botón llame al receptor "runTest" en lugar de llamar al método "actionPerformed" en la clase interna "RunTestButtonListener", que hace la llamada por usted).

Sin embargo, parece ser a costa de la necesidad de una verificación adicional por parte del desarrollador de que la llamada que creen que están haciendo está en el objeto correcto con el tipo correcto y pasando los parámetros correctos en el orden correcto, porque el compilador podría no advierte y se ejecutará perfectamente bien en tiempo de ejecución (solo devuelve una respuesta predeterminada). También podría decirse que hay un éxito en el rendimiento de la búsqueda adicional y el paso de parámetros.

En estos días, los lenguajes escritos dinámicamente pueden dar muchos de los beneficios del mensaje pasado OO con menos problemas.

Gavin H
fuente
1
Me gusta esta respuesta: explica las diferencias y sus implicaciones.
HappyCat
@Gavin, entonces, ¿es exactamente lo mismo que el controlador de métodos dinámicos de PHP y Javascript ?
Pacerier
11

Las arquitecturas de paso de mensajes son simplemente sistemas en los que cada componente es independiente de los demás, con un mecanismo común para pasar datos entre ellos. Puede considerar las llamadas a métodos como una forma de pasar mensajes, pero no es práctico hacerlo, confunde el problema. Esto se debe a que si tiene una clase con métodos bien definidos, y algún código que llama a esos métodos, todo se debe compilar, acoplando así el código y el objeto. puede ver cómo está cerca (cuando se pasa un mensaje y el compilador impone la corrección, pero pierde gran parte de la flexibilidad de un sistema desacoplado).

Las arquitecturas de paso de mensajes a menudo permiten agregar objetos en tiempo de ejecución, y la mayoría de las veces permiten que los mensajes se redirijan a uno o más objetos. Por lo tanto, puedo tener un código que transmita un mensaje de 'datos x actualizados' a todos los objetos que se han cargado en el sistema, y ​​cada uno de ellos puede tomar las medidas que desee con esa información.

Un extraño ejemplo es la web. HTTP es un sistema de paso de mensajes: pasa un verbo de comando y un 'paquete de datos' a un proceso del servidor. (p. ej. GET http: \ myserver \ url) Ni a su navegador ni al servidor web le importan los datos que envía, ni a dónde los envía. El servidor lo pasará al código que empaquetará otro 'paquete' de datos y se lo enviará de vuelta. Ninguno de los componentes de este sistema sabe nada sobre el trabajo de los demás o lo que hacen, solo conocen el protocolo utilizado para la comunicación del mensaje.

gbjbaanb
fuente
@gbjbannb, necesito algunas explicaciones de pseudocódigo ....
Pacerier