Smalltalk в примерах/Сообщения
Материал из Викиучебника
Сообщения
Как мы упоминали в предыдущей главе, мы посылаем сообщения объектам чтобы они что-либо сделали. В Smalltalk существует три вида сообщений.
Содержание |
[править] Унарные сообщения
Унарное сообщение - это сообщение без аргументов. Например, чтобы сменить знак целого числа мы должны послать унарное сообщение минус.
4 минус
[править] Бинарные сообщения
Бинарные. сообщения подобны арифметическим операциям. Они называются бинарными потому что они требуют два объекта.
5 + 3
Здесь мы послали сообщение + с аргументом 3 пятёрке. В отличии от многих языков где +, -, * и т.д. --- символы операторов связанные с алгебраическими операцыями, в Smalltalk они являются сообщениями с одним аргументом. Бинарными являются следующие сообщения
+ - * / ** // \\ < <= > >= = ~= == ~~ & | , @ ->
Многие из них очевидны, но есть и не совсем очевидные. ** возведение в степень
// целочисленное деление, округление до меньшего целого
\\ деление по модулю, возвращает остаток от деления
~= не равно
== идентичность, т.е. один и тот же объект
& логическое и, возвращает истину если оба объекта получатель и аргумент истинны. Получатель и аргумент оба вычисляются, поэтому лучше пользоваться сообщением и:, которое вычисляет только то что необходимо и оно также compileed in-line. Заметьте что оба сообщения & и и:, если получатель истинен, возвращают аргумент, который может и не принадлежать классу Логический.
| логическое или, возвращает истину если получатель или аргументы истинны. Получатель и аргумент оба вычисляются, поэтому лучше пользоваться сообщением или:, которое вычисляет только то что необходимо и оно также compileed in-line. Заметьте что оба сообщения | и или:, если получатель ложен, возвращают аргумент, который может и не принадлежать классу Логический.
, соединение двух совокупностей. Обычно используется для объединения строк.
@ используется для создания экземпляра класса Точка.
-> используется для создания экземпляра класса Association.
[править] Сообщения с ключевым словом
Мы всё ещё нуждаемся в другом типе сообщений с одним аргументом и более чем с одним аргументом. Сообщения с ключевым словом являются таковыми. Например:
'слон' копироватьОт: 2 до: 4
возвращает строку 'лон' (В Smalltalk совокупности индексируются начиная с 1 в отличии от C и C++ в которых индексы начинаются с 0; т.е. первый элемент совокупности нумеруется единицей). Двоеточие разделяет слова в сообщении, в котором каждое слово имеет аргумент. (Строго говоря, копироватьОт: 2 до: 4 это сообщение а копироватьОт:до: это селектор сообщения, но мы также будем называть копироватьОт:до: сообщением.)
[править] Последовательные сообщения
Методы обычно возвращают объект (подробнее об это позже). Это значит что вы можете посылать сообщения последовательно, потому что гарантируется что возвращается объект при посылки каждого сообщения. Например, следующая строка возвращает -3.
3.14 усечь минус
Когда мы посылаем числу с плавающей точкой сообщение усечь, оно возвращает МалоеЦелое, которое в свою очередь возвращает другое МалоеЦелое когда получает сообщение минус. Другим примером может быть строка которая содержит число. Мы хотим сменить знак числа и обратно перевести его в строку. Одним из вариантов этого действия будет:
число := '42' какЧисло. минусЧисло := число минус. строка := минусЧисло строкаДляПечати.
Однако, из за того что каждый метод возвращает объект мы можем написать данный пример как:
строка := ( ( '42' какЧисло ) минус ) строкаДляПечати.
Или мы можем опустить скобки пока мы работаем только с унарными сообщениями, все из которых имеют одинаковый приоритет.
строка := '42' какЧисло минус строкаДляПечати.
[править] Приоритет сообщений
В отличии от C++, в котором есть очень сложные правила приоритете, в Smalltalk они очень простые:
- Вычисление происходит слева направо.
- Унарные сообщения имеют высший приоритете.
- Бинарные сообщения следующие по приоритету.
- Сообщения с ключевым словом имеют наинизший приоритет.
- Вы можете изменить приоритет используя скобки.
Самое большое отличие от других языков это то что нет алгебраических операций. + и * это не алгебраические операции --- они просто сообщения. Используя вышеприведённые правила приоритета,
1 + 2 * 3
равно 9, а не 7. Чтобы получить результат, который вы ожидаете, вы должны использовать скобки.
1 + (2 * 3)
Ещё один пример, 2 + '4' какЧисло максимум: 5 даёт 6, потому что в соответствии с правилами, унарное сообщение какЧисло даёт
2 + 4 максимум: 5
Затем бинарное сообщение + даёт 6 максимум: 5, которое даёт 6.
Вычисление
30 максимум: 3 + 4 * 5
даёт 35. Нет унарных сообщений, Но бинарные сообщения выполняются слева направо. После отправления первого сообщения получаем:
30 максимум: 7 * 5
После следующего: 30 максимум: 35, которое возвращает 35. Чтобы получить ответ 30, как вы и ожыдали используя обычную алгебру, вы должны использовать скобки 30 максимум: 3 + (4 * 5).
[править] Что происходит когда посылается сообщение
Когда сообщение посылается объекту, Smalltalk смотрит, существует ли метод с таким именем для текущего типа объекта (другими словами, был написан и помещён в класс объекта). Если такой метод есть, он выполняется. Если такого метода нет, этот метод ищется в непосредственном суперклассе. Если это метода нет в суперклассе он ищется в суперклассе суперкласса. Рисунок~\ref{mehanizm} иллюстрирует это.
\begin{figure}[!htb]
\begin{center}
\includegraphics{mehanizm.eps}
\end{center}
\caption{Механизм нахождения метода}
\label{mehanizm}
\end{figure}
Метод ищется в иерархии суперклассов до тех пор пока не найдётся метод с этим именем, который и выполняется. Если достигнут класс Объект и в нём не найден метод, Объект показывает окно Сообщения позволяея тебе запустить отладчик и узнать что произошло\footnote{Что в действительности происходит когда метод не найден, посылается сообщение неПонял:. Если этот метод не переопределён он заставляет Объект вызвать исключение. !!!}.
[править] Получатель сообщения
Все сообщения должны посылаться объектам --- не существует понятия сообщения самого по себе. Если ns создал объект в методе, очень просто послать ему сообщение. Например:
МойКласс>>делайЭто массив := Массив новый: 3. массив от: 1 поместить: 2.
(Обычно используемое обозначение указания имени метода стороны экземпляра: ИмяКласса>>имяМетода. Для методов стороны класса: класс ИмяКласса>>имяМетода.)
[править] сам
Средняя длинна метода в Smalltalk 7 строк, поэтому, для того чтобы объект сделал серьёзную работу, хорошей практикой является разделение работы между несколькими методами (надеюсь, ты хочешь иметь короткие методы). Как вызвать другой метод, определённый в том же объекте? Ответ: объект посылает сообщение самому себе. В Smalltalk есть для этого специальная переменная --- сам --- которая всегда ссылается на объект который вызвал код --- получатель сообщения. Заметьте что сам ссылается на получателя даже когда код был определён в суперклассе класса получателя.
МойКласс>>обработатьОбъект: аОбъект сам делайЭтоСОбъектом: аОбъект. сам делайТоСОбъектом: аОбъект.
Если метод нуждается в другом методе для выполнения некоторой работы, пошлите сообщения сам. Фактически, если вы не знаете какому объекту послать сообщение, хорошим правилом будет послать его сам.
[править] супер
Если вы помните как ищется сообщение, Smalltalk сначала ищет метод в объекте которому пришло сообщение. Если здесь сообщение не найдено, он затем ищет его в суперкласс и т.д. Но что мы должны делать если хотим сразу начать искать сообщение в суперклассе? Smalltalk предоставляет другую специальную переменную, супер. Таким образом, если вы хотите начать с суперкласса, пошлите сообщение супер.
Когда супер должна использоваться? Один из основных примеров это создание экземпляра. Если вы хотите инициализировать переменные экземпляра вы обычно пишете метод инициализировать на стороне экземпляра. Вы не можете наследовать метод новый пока у вас нет метода инициализация, но вы должны написать свой собственный метод новый, который будет наследовать поведение метода новый от суперкласса. Знак ^, показанный ниже, означает возвращаемое значение.
МойКласс>>иницыализацыя ... присвоение некоторых переменных ...
класс МойКласс>>новый ^супер новый иницыализацыя
Фактически, супер не ссылается на суперкласс объекта которому пришло сообщение. Вместо этого, он ссылается на суперкласс объекта в котором определён выполняемый код. Это тонкое различие, но очень важное, потому что если бы это было не так, было бы легко получить бесконечную рекурсию. Давайте посмотрим почему. Пусть мы имеем иерархию классов КлассДва подкласс КлассаОдин, и КлассТри подкласс КлассаДва, как показано на Рисунке~\ref{ierarhix123}.
\begin{figure}[!htb]
\begin{center}
\includegraphics{ierarhix123.eps}
\end{center}
\caption{Иерархия наследования КлассаОдин, КлассаДва и КлассаТри}
\label{ierarhix123}
\end{figure}
Все три класса имеют переменные экземпляра, которые должны быть инициализированы, и код, делающий это, выглядит примерно как следующий.
КлассОдин>>инициализацыя ... присвоение некоторым переменным ...
КлассДва>>инициализацыя супер инициализацыя ... присвоение некоторым переменным ...
КлассТри>>инициализацыя супер инициализацыя ... присвоение некоторым переменным ...
Когда мы создаём экземпляр КлассаТри и выполняем код инициализация КлассаДва из объекта КлассаТри, на что ссылается супер? Если он ссылается на суперкласс класса выполняющего код, тогда суперклассом должен быть КлассДва, и сообщение инициализация должно быть снова послано КлассуДва. Т.е., мы попали в бесконечный цикл. С другой стороны, если супер ссылается на суперкласс класса, в котором определён код, сообщение должно быть послано КлассуОдин и бесконечного цикла не возникает.
Ключевым моментом, который надо отметить, является то что сам идентифицирует себя самого и может быть просмотрен, так же как переменная, и быть параметром. Однако, супер - это не идентификатор и вы не можете просмотреть его и использовать в качестве параметра. Когда вы выполняете метод, компилятор переводит текст в байткод. Когда он встречает слово супер, он создаёт код который заставляет среду выполнения искать метод в суперклассе класса, определившего метод. Поэтому супер - это просто механизм для сообщения компилятору как создавать байткод.