co to jest przekazywanie wiadomości w OO?

35

Studiowałem programowanie OO, głównie w C ++, C # i Javie. Myślałem, że dobrze to rozumiem, rozumiejąc enkapsulację, dziedziczenie i polimorfizm (a także czytając wiele pytań na tej stronie).

Jedną rzeczą, która wydaje się pojawiać tutaj i jest koncepcja „przekazywania wiadomości”. Najwyraźniej jest to coś, co nie jest używane podczas programowania OO we współczesnych językach głównego nurtu, ale jest obsługiwane przez Smalltalk.

Moje pytania to:

  • Co to jest przekazywanie wiadomości? (Czy ktoś może podać praktyczny przykład?)
  • Czy jest jakieś wsparcie dla tego „przekazywania wiadomości” w C ++, C # lub Javie?
Tomek
źródło
4
Odpowiedziałem na to pytanie jakiś czas temu: stackoverflow.com/a/3104741/10259
Frank Shearar
1
Czy czytałeś artykuł z Wikipedii ?
yannis
4
Moim skromnym zdaniem Objective-C kwalifikuje się jako język głównego nurtu. Przynajmniej tyle, ile C #.
mouviciel
Zgadzam się z tym, mówiłem o językach „głównego nurtu” z mojego doświadczenia
Tom
Wywołanie funkcji członka jest jedną z realizacji przekazywania wiadomości. Przekazany komunikat jest identyfikowany przez nazwę funkcji i zawiera informacje z parametrów. Późne wiązanie umożliwia klasie odbierającej obsługę tej samej wiadomości w inny sposób niż w przypadku innych klas. Nie było to zamierzone przez twórców Simuli, a wiele osób sprzeciwiłoby się nazywaniu go przekazywaniem wiadomości i twierdził (z uzasadnionego powodu), że przekazywanie wiadomości jest kluczową rzeczą, która odróżnia Simulę, ale wywołania funkcji członka nadal są zasadniczo takie same praca.
Steve314,

Odpowiedzi:

60

Co to jest przekazywanie wiadomości? (Czy ktoś może podać praktyczny przykład?)

Przekazywanie wiadomości oznacza po prostu, że (na bardzo abstrakcyjnym poziomie) podstawowym mechanizmem wykonywania programu są obiekty wysyłające sobie komunikaty. Ważną kwestią jest to, że nazwa i struktura tych wiadomości niekoniecznie muszą być wcześniej ustalone w kodzie źródłowym i same mogą być dodatkowymi informacjami. Jest to ważna część tego, co Alan Kay pierwotnie przewidywał jako „programowanie obiektowe”.

Czy jest jakieś wsparcie dla tego „przekazywania wiadomości” w C ++, C # lub Javie?

Te języki implementują ograniczoną wersję komunikatów przechodzących przez wywołania metod. Ograniczony, ponieważ zestaw wiadomości, które można wysłać, jest ograniczony do metod zadeklarowanych w klasie. Zaletą tego podejścia jest to, że można go bardzo skutecznie wdrożyć i umożliwia bardzo szczegółową analizę kodu statycznego (co daje wiele przydatnych korzyści, takich jak uzupełnianie kodu).

I odwrotnie, języki, które sugerują „prawdziwe” przekazywanie wiadomości, często mają również definicje metod, jako wygodny sposób na implementację procedur obsługi komunikatów, ale pozwalają klasom na implementację bardziej elastycznych procedur obsługi komunikatów, które umożliwiają obiektowi odbieranie „wywołań metod” o dowolnych nazwach (nie naprawiono w czasie kompilacji).

Przykład w Groovy, który pokazuje siłę tej koncepcji:

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

wyprodukuje ten 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>

Należy zauważyć, że records, car, countryi recordsą składniowo wywołania metod, ale nie istnieją żadne metody określonej w tej nazwie MarkupBuilder. Zamiast tego ma funkcję przechwytywania komunikatów catchall, która akceptuje wszystkie wiadomości i interpretuje nazwy wiadomości jako nazwę elementu XML, parametry jako atrybuty, a zamknięcia jako elementy potomne.

Michael Borgwardt
źródło
+1 bezpośrednio do odpowiedzi punktowej. Zaakceptowano w przykładzie kodu. Dzięki za pomoc :)
Tom
Czy nie można tego po prostu zaimplementować za pomocą prostych sendMessage(property_name, Array of arguments)i getMessage(property_name, Array of arguments)statycznych języków?
Pacerier
1
@Pacerier: jasne, ale łączy w sobie wady obu podejść - tracisz bezpieczeństwo typu i wciąż masz „sendMessage” zanieczyszczający twój kod wszędzie, więc nie dostajesz eleganckiej składni.
Michael Borgwardt
Czy bardziej poprawne byłoby stwierdzenie, że w przykładzie Groovy moduł obsługi komunikatów odbiera komunikat, a nie wywołanie metody? Początkowo umieszczasz cudzysłowy wokół wyrażenia „wywołanie metody”, ale w ostatnim zdaniu mówisz, że „akceptuje wszystkie metody ”, a nie „wiadomości”.
Adam Zerner,
@AdamZerner: masz rację, naprawiłem to.
Michael Borgwardt,
28

Przekazywanie wiadomości to inny sposób radzenia sobie z potrzebą w kodzie OO, aby jeden obiekt uzyskał inny obiekt (lub potencjalnie sam) do zrobienia czegoś.

W większości współczesnych języków wywodzących się z podejścia C ++ robimy to za pomocą wywołań metod. W tym przypadku wywoływany obiekt (poprzez swoją definicję klasy) umieszcza dużą listę akceptowanych wywołań metod, a następnie koder obiektu wywołującego po prostu zapisuje wywołanie:

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

W przypadku języków o typie statycznym kompilator może następnie sprawdzić typ wywoływanej rzeczy i potwierdzić, że metoda została zadeklarowana. W przypadku języków dynamicznie wpisywanych odbywa się to w czasie wykonywania.

Ale w gruncie rzeczy dzieje się tak, że pakiet zmiennych jest wysyłany do określonego bloku kodu.

Przekazywanie wiadomości

W językach przekazujących wiadomości (takich jak Cel C) zamiast metod istnieją odbiorniki, ale ogólnie podejście do ich definiowania i wywoływania jest takie samo - różnica polega na sposobie, w jaki jest obsługiwany.

W przekazywanym języku kompilator może sprawdzić, czy odbiornik, do którego zadzwoniłeś, istnieje, ale w najgorszym przypadku wyświetli ostrzeżenie, że nie jest pewien, czy tam jest. Wynika to z faktu, że w czasie wykonywania nastąpi wywołanie bloku kodu na obiekcie odbierającym, przekazując zarówno pakiet zmiennych, jak i podpis odbiorcy, który chcesz wywołać. Ten blok kodu następnie szuka odbiornika i wywołuje go. Jeśli jednak odbiornik nie istnieje, kod po prostu zwróci wartość domyślną.

W rezultacie jedną z osobliwości stwierdzonych podczas przechodzenia z C ++ / Java -> Cel C jest zrozumienie, że można „wywołać metodę” na obiekcie, który nie został zadeklarowany w typie czasu kompilacji, a nawet nie istniał typ wykonania ... i że wywołanie nie spowodowałoby wyrzucenia wyjątku, ale w rzeczywistości wynik zostałby przekazany z powrotem.

Zaletami tego podejścia jest to, że spłaszcza hierarchię podklasy i pozwala uniknąć większości potrzeb związanych z interfejsami / wielokrotnym dziedziczeniem / typami kaczek. Pozwala także obiektom zdefiniować domyślne zachowanie, gdy zostanie poproszony o zrobienie czegoś, dla czego nie ma odbiornika (zwykle „jeśli tego nie zrobię, przekaż żądanie do tego innego obiektu”). Może także uprościć łączenie z wywołaniami zwrotnymi (np. Dla elementów interfejsu użytkownika i zdarzeń czasowych), szczególnie w przypadku języków o typie statycznym, takich jak Java (dzięki czemu przycisk może wywoływać odbiornik „runTest” zamiast wywoływać metodę „actionPerformed” w klasie wewnętrznej „RunTestButtonListener”, który wykonuje wywołanie dla Ciebie).

Wydaje się jednak, że kosztem jest dodatkowe sprawdzenie przez dewelopera, czy wywołanie, które według nich wykonują, znajduje się na właściwym obiekcie z odpowiednim typem i przekazuje odpowiednie parametry we właściwej kolejności, ponieważ kompilator może nie ostrzeże cię, a będzie działał idealnie dobrze w czasie wykonywania (po prostu zwraca domyślną odpowiedź). Można również przypuszczać, że poprawiono wydajność dzięki dodatkowemu spojrzeniu i przekazywaniu parametrów.

Obecnie dynamicznie pisane języki mogą dawać wiele korzyści z OO przekazywanych z mniejszą ilością problemów.

Gavin H.
źródło
1
Podoba mi się ta odpowiedź - wyjaśnia różnice i ich implikacje.
HappyCat,
@Gavin, więc czy jest dokładnie taki sam jak dynamiczny moduł obsługi metod PHP i JavaScript ?
Pacerier
11

Architektury przekazywania wiadomości to po prostu systemy, w których każdy komponent jest niezależny od innych, ze wspólnym mechanizmem przekazywania danych między nimi. Możesz traktować wywołania metod jako formę przekazywania wiadomości, ale nie jest to praktyczne - powoduje to zamieszanie. Dzieje się tak dlatego, że jeśli masz klasę z dobrze zdefiniowanymi metodami i jakiś kod, który wywołuje te metody, cała sprawa musi zostać skompilowana razem, łącząc w ten sposób kod i obiekt. możesz zobaczyć, jak jest blisko (gdy wiadomość jest przekazywana, a kompilator wymusza poprawność, ale traci znaczną część elastyczności systemu odsprzęgniętego).

Architektury przekazywania wiadomości często pozwalają na dodawanie obiektów w czasie wykonywania, a częściej na przekazywanie wiadomości do jednego lub większej liczby obiektów. Mogę więc mieć kod, który rozgłasza komunikat „data x is updated” do wszystkich obiektów, które zostały załadowane do systemu, i każdy z nich może podjąć dowolne działanie z tymi informacjami.

Dziwnym przykładem jest sieć. HTTP to system przekazywania wiadomości - przekazujesz komendę czasową i „pakiet danych” do procesu serwera. (np. GET http: \ myserver \ url) Ani twoja przeglądarka, ani serwer internetowy nie dbają o dane, które wysyłasz lub do których je wysyłasz. Serwer przekaże go do kodu, który spakuje kolejny „pakiet” danych i odeśle go z powrotem. Żaden ze składników tego systemu nie wie nic o pracy innych ani o tym, co robią, po prostu znają protokół używany do komunikacji wiadomości.

gbjbaanb
źródło
@gbjbannb, Potrzebujesz wyjaśnień pseudokodu ....
Pacerier