qual è il messaggio che passa in OO?


35

Ho studiato la programmazione OO, principalmente in C ++, C # e Java. Pensavo di averne una buona comprensione con la mia comprensione dell'incapsulamento, dell'eredità e del polimorfismo (oltre a leggere molte domande su questo sito).

Una cosa che sembra apparire qua e là è il concetto di "passaggio di messaggi". Apparentemente, questo è qualcosa che non viene utilizzato durante la programmazione OO nelle lingue tradizionali di oggi, ma è supportato da Smalltalk.

Le mie domande sono:

  • Cosa sta passando il messaggio? (Qualcuno può fare un esempio pratico?)
  • Esiste un supporto per questo "passaggio di messaggi" in C ++, C # o Java?

4
Ho risposto a questa domanda su SO qualche tempo fa: stackoverflow.com/a/3104741/10259
Frank Shearar

1
Hai letto l' articolo di Wikipedia ?
yannis,

4
Secondo la mia modesta opinione, Objective-C si qualificherebbe come un linguaggio tradizionale. Almeno quanto C #.
mouviciel,

Sono d'accordo con questo, stavo affermando le lingue "mainstream" dalla mia esperienza
Tom

Una chiamata di funzione membro è un'implementazione del passaggio di un messaggio. Il messaggio passato è identificato dal nome della funzione e include le informazioni dai parametri. L'associazione tardiva consente alla classe ricevente di gestire lo stesso messaggio in modo diverso rispetto alle altre classi. Non è ciò che intendevano i creatori di Simula, e molte persone obietterebbero di chiamarlo passaggio di messaggi e affermano (con una buona ragione) che fare il passaggio di messaggi è una cosa chiave che rende diversa Simula, ma le chiamate di funzioni membro fanno sostanzialmente lo stesso lavoro.
Steve314

Risposte:


60

Cosa sta passando il messaggio? (Qualcuno può fare un esempio pratico?)

Il passaggio di messaggi significa semplicemente che (a un livello molto astratto) il meccanismo fondamentale dell'esecuzione del programma sono gli oggetti che si scambiano messaggi. Il punto importante è che il nome e la struttura di questi messaggi non sono necessariamente corretti in anticipo nel codice sorgente e possono essere informazioni aggiuntive. Questa è una parte importante di ciò che Alan Kay inizialmente concepiva come "programmazione orientata agli oggetti".

Esiste un supporto per questo "passaggio di messaggi" in C ++, C # o Java?

Questo linguaggio implementa una versione limitata del messaggio che passa attraverso le chiamate di metodo. Limitato perché l'insieme di messaggi che è possibile inviare è limitato ai metodi dichiarati in una classe. Il vantaggio di questo approccio è che può essere implementato in modo molto efficiente e consente un'analisi del codice statico molto dettagliata (che si traduce in tutti i tipi di vantaggi utili, come il completamento del codice).

Al contrario, i linguaggi che impiantano il passaggio di messaggi "reali" spesso presentano anche definizioni di metodo, come modo conveniente per implementare gestori di messaggi, ma consentono alle classi di implementare gestori di messaggi più flessibili che consentono all'oggetto di ricevere "chiamate di metodo" con nomi arbitrari (non fisso al momento della compilazione).

Un esempio in Groovy che dimostra la potenza di questo concetto:

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')
  }
}

produrrà questo 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>

Si noti che records, car, countrye recordsono sintatticamente chiamate di metodo, ma non ci sono metodi di quel nome definito MarkupBuilder. Al contrario, ha un gestore di messaggi catchall che accetta tutti i messaggi e interpreta i nomi dei messaggi come il nome di un elemento XML, i parametri come attributi e le chiusure come elementi figlio.


+1 direttamente alla risposta punto. Accettato per l'esempio di codice. Grazie per l'aiuto :)
Tom

Quindi non potrebbe essere semplicemente implementato con un linguaggio semplice sendMessage(property_name, Array of arguments)e getMessage(property_name, Array of arguments)statico?
Pacerier,

1
@Pacerier: certo, ma questo combina gli svantaggi di entrambi gli approcci: perdi la sicurezza dei tipi e hai ancora "sendMessage" che inquina il tuo codice ovunque, quindi non ottieni la sintassi elegante.
Michael Borgwardt,

Sarebbe più corretto affermare che, nell'esempio Groovy, il gestore messaggi sta ricevendo un messaggio, piuttosto che una chiamata di metodo? Inizialmente metti delle virgolette intorno alla frase "chiamata metodo", ma nell'ultima frase, dici che "accetta tutti i metodi ", piuttosto che "messaggi".
Adam Zerner,

@AdamZerner: hai ragione, l'ho risolto.
Michael Borgwardt,

28

Il passaggio di messaggi è un modo diverso di gestire la necessità nel codice OO affinché un oggetto ottenga un altro oggetto (o potenzialmente se stesso) per fare qualcosa.

Nella maggior parte dei linguaggi moderni che discendono dall'approccio C ++ lo facciamo con le chiamate di metodo. In questo caso l'oggetto chiamato (tramite la sua definizione di classe) mette un grande elenco di quale metodo chiama accetta e quindi il codificatore dell'oggetto chiamante semplicemente scrive la chiamata:

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

Per i linguaggi tipizzati staticamente, il compilatore può quindi controllare il tipo di cosa chiamata e confermare che il metodo è stato dichiarato. Per le lingue tipizzate dinamicamente, ciò viene eseguito in fase di esecuzione.

Ma in sostanza ciò che accade è che un fascio di variabili viene inviato a un blocco di codice specifico.

Messaggio che passa

Nei linguaggi che trasmettono messaggi (come l'obiettivo C) invece dei metodi ci sono ricevitori, ma in generale l'approccio di definirli e chiamarli è più o meno lo stesso: la differenza sta nel modo in cui viene gestito.

In una lingua di messaggio passata, il compilatore può verificare l'esistenza del destinatario che hai chiamato, ma nella peggiore delle ipotesi si aprirà un avviso per dire che non è sicuro che sia lì. Questo perché in fase di esecuzione ciò che accadrà è che un blocco di codice sull'oggetto ricevente verrà chiamato passando sia il pacchetto di variabili sia la firma del destinatario che si desidera chiamare. Quel blocco di codice cerca quindi il destinatario e lo chiama. Tuttavia, se il ricevitore non esiste, il codice restituirà semplicemente un valore predefinito.

Di conseguenza una delle stranezze rilevate quando ci si sposta da C ++ / Java -> Obiettivo C è capire che è possibile "chiamare un metodo" su un oggetto che non è stato dichiarato nel tipo di compilazione e che non esisteva nemmeno il tipo di runtime ... e che la chiamata non comporterebbe il lancio di un'eccezione ma in realtà un risultato che verrà restituito.

I vantaggi di questo approccio sono che appiattisce la gerarchia delle sottoclassi ed evita la maggior parte delle esigenze di interfacce / eredità multipla / tipi di anatre. Inoltre, consente agli oggetti di definire il comportamento predefinito quando viene chiesto loro di fare qualcosa per cui non hanno un ricevitore (comunemente "se non lo faccio, inoltra la richiesta a questo altro oggetto"). Può anche semplificare il collegamento ai callback (ad esempio per gli elementi dell'interfaccia utente e gli eventi a tempo) in particolare su linguaggi tipicamente statici come Java (quindi puoi avere il pulsante per chiamare il ricevitore "runTest" piuttosto che chiamare il metodo "actionPerformed" sulla classe interna "RunTestButtonListener" che fa la chiamata per te).

Tuttavia, sembrerebbe a costo della necessità di ulteriori controlli da parte dello sviluppatore che la chiamata che pensano di fare sia sull'oggetto giusto con il tipo giusto e che passi i parametri giusti nell'ordine giusto, perché il compilatore potrebbe non ti avverte e funzionerà perfettamente in fase di esecuzione (solo restituendo una risposta predefinita). Probabilmente c'è anche un colpo di performance dal look-up extra e dal passaggio dei parametri.

Al giorno d'oggi, i linguaggi tipizzati dinamicamente possono offrire molti vantaggi del messaggio passato OO con meno problemi.


1
Mi piace questa risposta - spiega le differenze e le loro implicazioni.
HappyCat,

@Gavin, Quindi è esattamente lo stesso del gestore di metodo dinamico di PHP e Javascript ?
Pacerier,

11

Le architetture di passaggio dei messaggi sono semplicemente sistemi in cui ciascun componente è indipendente dagli altri, con un meccanismo comune per il passaggio dei dati tra di loro. Puoi considerare le chiamate di metodo come una forma di passaggio di messaggi, ma non è pratico farlo - confonde il problema. Questo perché se si dispone di una classe con metodi ben definiti e del codice che chiama tali metodi, l'intera cosa deve essere compilata insieme, accoppiando così il codice e l'oggetto. puoi vedere come è vicino (mentre un messaggio viene passato e il compilatore impone la correttezza, ma perde gran parte della flessibilità di un sistema disaccoppiato).

Le architetture di passaggio dei messaggi consentono spesso di aggiungere oggetti in fase di esecuzione e, molto spesso, di reindirizzare i messaggi a uno o più oggetti. Quindi posso avere un po 'di codice che trasmette un messaggio "data x viene aggiornato" a tutti gli oggetti che sono stati caricati nel sistema e ognuno di loro può intraprendere qualsiasi azione che gli piaccia con quelle informazioni.

Un bizzarro esempio è il web. HTTP è un sistema di passaggio di messaggi: si passa un verbo di comando e un "pacchetto di dati" a un processo del server. (es. OTTIENI http: \ myserver \ url) Né il tuo browser, né il server web si preoccupano di nulla dei dati che invii o di dove li invii. Il server lo passerà al codice che impacchetterà un altro "pacchetto" di dati e te lo restituirà. Nessuno dei componenti di questo sistema è a conoscenza del funzionamento degli altri o di ciò che fanno, conoscono semplicemente il protocollo utilizzato per la comunicazione dei messaggi.


@gbjbannb, Hai bisogno di alcune spiegazioni di pseudo codice ....
Pacerier,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.