Все начинается с печени…
Давайте немного отвлечемся от программирования и поговорим о биологии. Точнее, клетки. Я обещаю, что это будет связано с передачей сообщений, просто оставайтесь со мной.
Это репост с сайта programmingwords.com. Это блог о различных темах информатики, которые объясняются простым и понятным языком. Проверьте это, если вы заинтересованы в compsci, компиляторах или языках программирования! :)
Обычно мы думаем о своем теле как о механизме из костей и органов, толкаемом и тянемом мышцами и аккуратно окутанном слоем кожи. Но когда вы увеличите масштаб, вы увидите гигантское море примерно из 10 триллионов клеток, которые хаотично плавают вокруг.
В вашем теле около 200 различных типов клеток, каждая из которых выполняет свою специфическую функцию. Например, ваша почка состоит из почечных клеток, работающих вместе и выполняющих… ну… то, что делает почка. (Я не биолог.)
Каждая из этих клеток примерно в 100 000 раз меньше вас, но каким-то образом эта густая смесь клеток умудряется работать вместе, чтобы шевелить моими пальцами, пока я печатаю это предложение. Ключом, как и во всей командной работе, является общение.
Клетки взаимодействуют друг с другом с помощью процесса, называемого передача клеточных сигналов. Один тип клеточной сигнализации называется эндокринная сигнализация, и вы можете думать о нем как о Slack, но для клеток: он позволяет удаленным клеткам работать вместе.
Давайте рассмотрим конкретный пример: вы напряженно работаете сверхурочно, чтобы уложиться в срок. Когда вы испытываете стресс, прямо над почками клетки надпочечников начинают выделять знакомую вам молекулу: адреналин. Адреналин — это довольно четкое сообщение о том, что «все вот-вот станет ужасно».
Адреналин проходит через кровоток к печени. Клетки вашей печени имеют рецепторы, которые могут получать адреналин. Как только они получают сообщение, они меняют свое поведение в ответ.
Видите ли, печень похожа на хранилище глюкозы, основного источника энергии вашего тела. Если дела вот-вот станут ужасными, лучше всего у вашего тела достаточно энергии. Таким образом, клетки печени реагируют на сообщение, производя больше глюкозы, давая вашим мышцам достаточно энергии, чтобы бороться или бежать.
Адреналин — это лишь одна из многих молекул, которые можно послать в виде сообщения. Клетки общаются, отправляя и получая эти молекулы. Принимающая клетка имеет специфические рецепторы, которые могут считывать эти молекулы. Как только сообщение получено, ячейка вносит изменения в соответствии с сообщением.
Если вы возьмете предыдущее предложение и замените «ячейка» на «объект», вы получите передачу сообщений.
Какое отношение печень имеет к программированию?
Передача сообщений — это способ, которым части вашей программы могут взаимодействовать друг с другом. При передаче сообщений каждый объект действует как независимая ячейка. Единственное, что объект может сделать для связи с другими объектами, это послать сообщение.
В основных языках ООП, таких как Java или C#, у вас будут объекты, которые определяют свои методы, которые вы вызываете для внесения изменений в эти объекты. Например, следующий код вызовет метод с именем setNumberOfUsers
для объекта с именем userStore
:
userStore.setNumberOfUsers(3)
Передача сообщений добавляет к этому процессу еще один уровень абстракции. При передаче сообщений вы достигли бы того же эффекта, отправив сообщение объекту:
sendMessage(userStore, "setNumberOfUsers:3")
Вместо предопределенного набора методов для вызова объекта вы отправляете объекту произвольное сообщение. Ответит ли объект на это сообщение или нет, зависит от объекта.
В приведенном выше примере сообщение представляет собой простую строку. Не существует понятия «определенные» методы, и компилятор не будет проверять существование метода. Вместо этого вы отправляете строку, и объект должен прочитать и ответить на строку.
Самая большая разница между обычным ООП и этим подходом заключается в том, кто владеет методами. В основных языках ООП методы живут в классе. Чтобы добавить новый метод, вы должны создать новый (под)класс.
Передача сообщений делает еще один шаг вперед, позволяя объектам самим решать, как интерпретировать и реагировать на сообщения, а также на какие сообщения отвечать. Два экземпляра одного и того же класса могут по-разному реагировать на одно и то же сообщение.
Это добавляет уровень гибкости при моделировании вашей программы. Вы когда-нибудь начинали писать класс и вызывали несуществующие на данный момент методы, которые будете реализовывать в будущем? Я все время это делаю. При передаче сообщений вызов еще не реализованного метода не приведет к ошибкам. Сообщение будет отправлено, но просто проигнорировано принимающим объектом.
Это позволило достичь уровня разделения, который невозможен с такими языками, как Java. Два фрагмента кода разъединяются, когда изменение одного не приводит к необходимости изменения другого. При передаче сообщений удаление, добавление или переименование сообщений не приводит к ошибкам компилятора. Объекты отделены до такой степени, что даже не зависят от имен методов.
Основные языки ООП требуют от вас заранее смоделировать свое программное обеспечение, создавая классы и методы еще до того, как вы их используете. Передача сообщений обеспечивает достаточную гибкость, чтобы дизайн вашего кода мог органично возникать и изменяться по мере необходимости.
Преимущества передачи сообщений аналогичны преимуществам динамического ввода. Фактически, для передачи сообщений в значительной степени требуется язык с динамической типизацией или, по крайней мере, отсутствие безопасности типов в языке со статической типизацией.
Конкретный пример
Алан Кей широко известен как один из тех, кто ввел термин «объектно-ориентированное программирование». Он изобрел не то, что сегодня есть в Java или C#, вместо этого краеугольным камнем его ООП была передача сообщений.
Идея Алана Кея, как и большинство идей, возникла из множества открытий, которые переплелись воедино, чтобы создать Smalltalk — язык программирования, популяризировавший ООП, самую популярную парадигму программирования.
Но Smalltalk выглядит иначе, чем Java. Во-первых, он динамически типизирован. Тот шум, который вы сейчас слышите, — это все, что вздрагивает Java-разработчиков. Он динамически типизирован, потому что использует передачу сообщений. Подумайте об этом, у вас не может быть статических предопределенных типов, если каждый объект может отвечать на произвольный набор сообщений.
Сейчас я покажу вам немного Smalltalk, и он, вероятно, будет выглядеть иначе, чем любой другой язык, который вы видели. Smalltalk был выпущен около 50 лет назад, поэтому его синтаксис отличается от языков с фигурными скобками на основе C, к которым мы привыкли.
Мы начнем с объявления переменной с именем num
. Объявления переменных окружены |
. (...для любой причины.)
| num |
Вы заметите, что здесь нет спецификаторов типа — помните, Smalltalk динамичен.
Затем мы присвоим значение этой переменной:
num := 42.
Как и во многих старых языках, в Smalltalk для присваивания используется более математический :=
вместо более распространенного =
. Чтобы закончить утверждение, мы будем использовать точку вместо точки с запятой, как в этом предложении.
Теперь возведем 42 в степень 2.
num := num raisedTo: 2.
На языке C это может выглядеть как num = pow(num, 2)
.
Вместо этого Smalltalk использует передачу сообщений. Здесь raisedTo: 2
— это сообщение, отправленное объекту num
. Smalltalk называет именованную часть сообщения селектором (raisedTo
), который может иметь свои аргументы, в данном случае число 2.
Разработчикам языков нравится передача сообщений из-за ее рекурсивного характера. Когда у вас есть передача сообщений, вы можете построить на ее основе весь язык. Например, выражение if/else
может быть ничем иным, как отправкой сообщения ifTrue
логическому объекту.
numberIs42 := (num = 42)
message := numberIs42
ifTrue:[ 'answer' ]
ifFalse:[ 'not the answer' ].
Строка выше присваивает «ответ» message
, если num
равно 42. num = 42
вычисляет логический объект, которому мы можем отправлять сообщения. Сообщение ifTrue:ifFalse:
имеет два аргумента, которые являются блоками кода, в данном случае каждый из которых возвращает строку.
Вместо того, чтобы if
и else
были специальными ключевыми словами, встроенными в синтаксис языка, здесь они не более чем простые сообщения, отправляемые объекту.
Даже создание классов — это не что иное, как отправка сообщений объекту!
Object subclass: 'Square'
instanceVariables: 'a'
Вот как вы создаете класс со свойством a
. Этот фрагмент кода отправляет сообщение subclass:instanceVariables:
глобально определенному объекту с именем Object
.
Java должен просмотреть все ваши классы и их методы, а затем скомпилировать код, привязав вызов метода к блоку кода. Smalltalk не делает ничего из этого во время компиляции, вместо этого вы отправляете сообщение Object
для создания класса во время работы программы. Правильно, вы можете создавать новые классы в середине выполнения вашего кода.
Эквивалентом методов Java в Smalltalk могут быть сообщения, которые могут получать объекты этого класса. Добавим пару таких.
a: newA a := newA
a: ^a
area: a * a
init: a := 1.
Вы заметите, что здесь нет свойств, методов или полей — это просто сообщения. «Геттер» — это сообщение с именем a
, которое после получения возвращает значение a. Точно так же сеттер — это сообщение, которое включает новое значение как часть сообщения. Даже «конструктор» — это просто старое сообщение под названием init
.
Smalltalk, созданный Аланом Кеем в 1972 году, сильно отличается от приведенного выше, и даже ему почти 40 лет. Не так много людей используют Smalltalk, но по воле судьбы его дух все еще живет среди программистов на языке под названием Objective-C.
Objective-C был создан примерно в то же время, что и C++, и долгое время эти два языка конкурировали за звание доминирующего языка ООП. В конце концов, как вы могли догадаться, C++ победил.
Но Objective-C живет в основном благодаря Стиву Джобсу. Покинув Apple, он основал компанию NeXT и создал операционную систему на базе Objective-C. 12 лет спустя Apple купила эту компанию и назначила Стива Джобса своим генеральным директором, который перенес все свое программное обеспечение на операционную систему NeXT. В результате более 20 лет спустя macOS, iOS и другие платформы Apple построены на основе огромной кодовой базы Objective-C.
Однако Objective-C — не единственный современный язык с передачей сообщений. Передача сообщений — это основная функция Ruby и Python, и ее можно использовать в других языках с библиотеками.
Настоящее ООП?
На протяжении всей этой статьи я сопоставлял общепринятый ООП с передачей сообщений. Но, как вы видели, этот термин был придуман для передачи сообщений. Как говорит Алан Кей:
Мне жаль, что я давно придумал термин «объекты» для этой темы, потому что он заставляет многих людей сосредоточиться на второстепенной идее.
Большая идея — «обмен сообщениями» […].
Если передача сообщений — это то, с чего началось ООП, почему никто больше не говорит о передаче сообщений в этом контексте? Ни в одном из моих курсов ООП в колледже не упоминалась передача сообщений. ООП всегда представляется как сочетание полиморфизма, наследования, инкапсуляции и абстракции. Все это существует в Smalltalk, но в основном как симптом передачи сообщений, а не встроенный в язык.
Правда в том, что примерно в то же время, когда разрабатывался Smalltalk, в Осло, Норвегия, разрабатывался другой язык под названием Simula. Этот язык был также ООП-языком (фактически, возможно, первым ООП-языком), хотя вначале он так не назывался. В нем не было передачи сообщений, вместо этого он полагался на то, что называется абстрактными типами данных.
Если вы работали с любым основным языком ООП, вы уже знаете, что такое абстрактные типы данных: классы. Simula представила почти все функции, которые мы сегодня считаем частью ООП, включая классы, объекты, наследование и даже сборку мусора.
Самая большая разница между Smalltalk и Simula заключается в том, что Simula использует типы. Даже сегодня, когда у нас есть огромное количество продвинутых линтеров и инструментов, помогающих с языками динамического программирования, многие программисты по-прежнему предпочитают языки ООП со статической типизацией, особенно для больших проектов. В то время инструменты для динамической типизации были еще менее развиты, поэтому само собой разумеется, что люди считали типы более продуктивными, чем дикий запад отправки сообщений и надежды, что кто-то где-то их получит.
Многие CS-ботаники будут постоянно напоминать вам, что Алан Кей не имел в виду C++, когда думал об ООП. Стоя здесь более 30 лет спустя, я не думаю, что это имеет значение. Термины существуют для того, чтобы быть полезными для передачи идеи, и кажется очевидным, что «ООП» означает больше Java, чем Smalltalk, даже если Алан Кей не представлял себе ООП таким образом.
Что не означает, что Smalltalk провалился. Языки иногда подобны фильмам. Независимые кинематографисты снимают впечатляющие фильмы, такие как «Бешеные псы» Тарантино, которые мало кто видит, но их влияние ощущается десятилетиями. Тем не менее, популярные фильмы, такие как «Мстители», представляют собой разбавленные комбинации этих влияний, своего рода общий знаменатель, который понравится почти всем.
Языки иногда могут быть такими же. Несмотря на то, что в Java нет передачи сообщений, на него все же сильно повлиял Smalltalk. На самом деле, почти на все языки ООП повлиял Smalltalk. Без Алана Кея язык, на котором вы программируете, вероятно, выглядел бы иначе.
Вот почему важно поощрять идеалистические языки программирования, основанные на видении. Они могут выглядеть странно, но будьте уверены, что в них есть идеи, которые обязательно распространятся на основные языки позже.
Это репост с сайта programmingwords.com. Это блог о различных темах информатики, которые объясняются простым и понятным языком. Проверьте это, если вы заинтересованы в compsci, компиляторах или языках программирования! :)
Примечание. В математике передача сообщений называется моделью акторов. Это очень важная модель параллельного программирования, но это история для другой статьи. :)
Использованная литература:
Электронное письмо от Алана Кея с описанием того, как он пришел к дизайну Smalltalk
http://www.purl.org/stefan_ram/pub/doc_kay_oop_en
Еще одно письмо по ООП от Алана Кея
http://lists.squeakfoundation.org/pipermail/squeak-dev/1998-October/017019.html
The Early History of Smalltalk, Алан Кей
(Статья об истории Smalltalk)
http://www.metaobject.com/papers/Smallhistory.pdf