що таке передача повідомлення в ОО?


35

Я вивчав програмування OO, головним чином на C ++, C # та Java. Я думав, що я добре зрозумів це з моїм розумінням інкапсуляції, успадкування та поліморфізму (а також з читанням багатьох питань на цьому сайті).

Одне, що, здається, з'являється тут і є концепція "передачі повідомлення". Мабуть, це те, що не використовується під час програмування OO в сучасних мовах, але підтримується Smalltalk.

Мої запитання:

  • Що передає повідомлення? (Чи може хтось навести практичний приклад?)
  • Чи є підтримка цього "проходження повідомлення" в C ++, C # або Java?

4
Я відповів на це питання на так давно: stackoverflow.com/a/3104741/10259
Frank Shearar

1
Ви читали статтю у Вікіпедії ?
Янніс

4
На мою скромну думку, «Objective-C» можна було б визнати основною мовою. Принаймні стільки, скільки C #.
mouviciel

Я згоден з цим, я викладав "основні" мови зі свого досвіду
Том,

Виклик функції члена - це одна реалізація передачі повідомлення. Повідомлення, що передається, ідентифікується ім'ям функції та включає інформацію з параметрів. Пізні прив’язки дозволяють класу прийому обробляти те саме повідомлення по-іншому, як і інші класи. Це не те, що було призначено творцями Simula, і багато людей заперечували б називати це передачею повідомлень і заявляють (з поважною причиною), що виконання передачі повідомлень є ключовою річчю, що робить Simula різною, але виклики функцій учасників все-таки роблять по суті те саме робота.
Steve314

Відповіді:


60

Що передає повідомлення? (Чи може хтось навести практичний приклад?)

Передача повідомлень просто означає, що (на дуже абстрактному рівні) основним механізмом виконання програми є об'єкти, що надсилають один одному повідомлення. Важливим моментом є те, що ім'я та структура цих повідомлень не обов'язково заздалегідь фіксуються у вихідному коді і можуть самі по собі бути додатковою інформацією. Це важлива частина того, що спочатку Алан Кей уявляв як "об'єктно-орієнтоване програмування".

Чи є підтримка цього "проходження повідомлення" в C ++, C # або Java?

Ці мови реалізують обмежену версію повідомлення, що проходить через виклики методів. Обмежено, оскільки набір повідомлень, які можна надіслати, обмежений методами, оголошеними в класі. Перевага такого підходу полягає в тому, що він може бути реалізований дуже ефективно, і він дозволяє дуже детально проаналізувати статичний код (що призводить до всіх видів корисних переваг, таких як заповнення коду).

І навпаки, мови, які використовують «реальні» передачі повідомлень, часто також мають визначення методів як зручного способу реалізації обробників повідомлень, але дозволяють класам реалізовувати більш гнучкі обробники повідомлень, які дозволяють об'єкту отримувати «виклики методу» з довільними іменами (не фіксовано під час компіляції).

Приклад у Groovy, який демонструє силу цієї концепції:

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

створить цю 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>

Слід зазначити , що records, car, countryі recordсинтаксично виклики методів, але немає методів цього імені , визначеного в MarkupBuilder. Натомість у нього є обробник повідомлень catchall, який приймає всі повідомлення та інтерпретує імена повідомлень як ім'я XML-елемента, параметри як атрибути та закриття як дочірні елементи.


+1 прямо до точкової відповіді. Прийнято для прикладу коду. Дякую за допомогу :)
Том,

Тож чи не можна було це просто реалізувати простою sendMessage(property_name, Array of arguments)та getMessage(property_name, Array of arguments)статичною мовами?
Pacerier

1
@Pacerier: впевнено, але це поєднує недоліки обох підходів - ви втрачаєте безпеку типу і все ще маєте "sendMessage", що забруднює ваш код скрізь, так що ви не отримуєте елегантний синтаксис.
Майкл Боргвардт

Чи було б правильніше сказати, що в прикладі Groovy обробник повідомлень отримує повідомлення, а не виклик методу? Ви спочатку ставите цитати навколо фрази "виклик методу", але в останньому реченні ви говорите, що воно "приймає всі методи ", а не "повідомлення".
Адам Зернер

@AdamZerner: ти маєш рацію, я це виправив.
Майкл Боргвардт

28

Передача повідомлень - це інший спосіб вирішення потреби в коді OO для одного об'єкта, щоб отримати інший об’єкт (або потенційно сам) зробити щось.

У більшості сучасних мов, які відходять від підходу C ++, ми робимо це з викликами методів. У цьому випадку викликаний об'єкт (через його визначення класу) містить великий список того, який метод викликів він приймає, і тоді кодер виклику об'єкта просто записує виклик:

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

Для статично набраних мов тоді компілятор може перевірити тип виклику речі та підтвердити, що метод був оголошений. Для динамічно набраних мов тоді це виконується під час виконання.

Але по суті, що відбувається - це те, що пакет змінних відправляється до конкретного блоку коду.

Повідомлення проходить

У мовах передачі повідомлень (таких як Objective C) замість методів є приймачі, але в цілому підхід їх визначення та виклику майже однаковий - різниця полягає у тому, як вони обробляються.

У мові переданого повідомлення компілятор може перевірити, чи існує приймач, який ви назвали, але в гіршому випадку з’явиться попередження, сказавши, що він не впевнений, що його є. Це тому, що під час запуску буде відбуватися те, що блок коду на об'єкті прийому буде називатися, передаючи як пакет змінних, так і підпис приймача, який ви хочете викликати. Потім блок коду шукає приймач і викликає його. Однак якщо приймача не існує, код просто поверне значення за замовчуванням.

В результаті одна з диваків, виявлених при переході з C ++ / Java -> Об'єктив C, розуміє, що ви можете "викликати метод" на об'єкт, який не був оголошений у типі часу компіляції і навіть не існував на тип запуску ... і що виклик не призведе до відкидання виключення, а насправді результат передачі назад.

Переваги такого підходу полягають у тому, що він згладжує ієрахію підкласу та уникає більшості потреб у інтерфейсах / декількох типах успадкування / качок. Він також дозволяє об'єктам визначати поведінку за замовчуванням, коли його просять зробити щось, для чого вони не мають приймача (як правило, "якщо я цього не роблю, пересилайте запит цьому іншому об'єкту"). Він також може спростити зв'язок із зворотними дзвінками (наприклад, для елементів інтерфейсу користувача та приурочених подій), особливо щодо статично типових мов, таких як Java (так що ви можете мати кнопку виклику приймача "runTest", а не викликати метод "actionPerformed" у внутрішньому класі "RunTestButtonListener", який виконує вам дзвінок).

Однак, здавалося б, ціною необхідності додаткової перевірки розробником є ​​те, що дзвінок, який вони думають, що вони здійснюють, знаходиться на потрібному об'єкті з правильним типом і передає правильні параметри в потрібному порядку, тому що компілятор може не попередити вас, і він буде працювати чудово під час виконання (лише повернення відповіді за замовчуванням). Можливо, також є хіт виступу від додаткового огляду та параметри.

У наші дні динамічно набрані мови можуть принести багато переваг повідомленням, що передаються OO, з меншою кількістю питань.


1
Мені подобається ця відповідь - пояснює відмінності та їх наслідки.
HappyCat

@Gavin, Тож це точно так само, як динамічний метод обробки PHP та Javascript ?
Pacerier

11

Архітектури передачі повідомлень - це просто системи, де кожен компонент не залежить від інших, має загальний механізм передачі даних між ними. Ви можете розглядати виклики методів як форму передачі повідомлення, але це не практично - це плутає проблему. Це тому, що якщо у вас є клас із чітко визначеними методами та деяким кодом, який викликає ці методи, вся справа повинна бути складена разом, таким чином з'єднуючи код і об'єкт. ви можете бачити, наскільки воно близьке (як передається повідомлення, і компілятор виконує правильність, але він втрачає значну частину гнучкості роз'єднаної системи).

Архітектури передачі повідомлень часто дозволяють додавати об'єкти під час виконання, і частіше за все не дозволяють повідомлення перенаправляти на один або кілька об'єктів. Тому я можу мати якийсь код, який передає повідомлення "дані x оновлюється" для всіх об'єктів, завантажених у систему, і кожен з них може вжити будь-яких дій, що їм подобається з цією інформацією.

Дивний приклад - Інтернет. HTTP - це система передачі повідомлень - ви передаєте командне дієслово та "пакет даних" серверовому процесу. (наприклад, GET http: \ myserver \ url) Ні ваш браузер, ні веб-сервер нічого не цікавить про дані, які ви надсилаєте, або про те, де ви надсилаєте їх. Сервер передасть його до коду, який буде пакувати ще один "пакет" даних та відправляти його вам назад. Жоден компонент у цій системі нічого не знає про роботу інших або про те, що вони роблять, вони просто знають протокол, який використовується для повідомлення повідомлень.


@gbjbannb, Потрібні пояснення псевдокоду ....
Pacerier
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.