Smalltalk по-русски
Advertisement
6.0

Протокол для всех объектов[]

6.diagrama

Всё что есть в системе это объекты. Протокол общий для всех объектов системы предоставлен в описании класса Объект. Это значит что любой объект созданный системой может отвечать на сообщения определённые в классе Объект. Обычно это сообщения которые поддерживают разумное поведение по умолчанию для предоставления начальной точки с которой можно продолжать создавать новые виды объектов, либо добавляя новые сообщеня либо изменяя ответ на существующие сообщения. Examples to consider when examining Object's protocol are объекты числа такие как 3 или 16.23, совокупности такие как 'this is a string' или #( #это #ряд ), пусто или истина, и объектов описывающих класс таких как Совокупность или Малое целое или, даже, самого Объекта.

Описание протокола для класса Объект данное в данной главе не полно. Мы опустили сообщения принадлежащие обработке сообщений, special dependency ralationships, и примитивам системы. Они представлены в главе 14.

Проверка функциональности объекта[]

Каждый объект это экземпляр класса. Функциональность объекта определяется его классом. Эта функциональность проверяется двумя способами: явное указание класса для определения того что это класс или суперкласс объекта, и указание селекторов сообщений для определения может ли объект отвечать на них. Эти два способа отражают два способа мышления о взаимосвязи между экземплярами различных классов: в терминах ирархии классов/подклассов, или в терминах разделяемых протоколов сообщений.

Протокол экземпляров Объекта
проверка функциональности
класс возвращает объект являющийся классом получателя.
это разновидность: класс отвечает является ли аргумент, класс, суперклассом или классом получателя.
это член: класс отвечает является ли получатель прямым экземпляром аргумента, класс. Это то же самое что проверить является ли ответ на сообщение класс тем же (==) что и аргумент класс.
отвечает на: символ отвечает содержит ли словарь класса получателя или его суперкласса аргумент, символ, в качестве селектора сообщения.

Примеры сообщений и их результатов

предложение значение
3 класс. Малое целое
#( #это #ряд ) это разновидность: Набор. истина
#( #это #ряд ) это член: Набор. ложь
#( #это #ряд ) класс. Ряд
3 отвечает на: #это разновидность:. истина
#( 1 2 3 ) это член: Ряд. истина
Объект класс. Объект класс

Сравнение объектов[]

Т.к. вся информация в системе представлена объектами, то существует основной протокол для проверти идентичности объектов и для копирования объектов. Важные методы сравнения определённые в классе Объект это тесты на эквивалентность и на равенство. Эквивалентность (==) это проверка являются ли два объект одним и тем же объектом. Равенство (=) это проверка на то представляют ли объекты одинаковые компоненты. Смысл фразы "представляют ли объекты одинаковые компоненты" зависит от получателя сообщения; каждый новый вид объектов который добавляет переменные экземпляра обычно должен переопределить сообщение = чтобы определить какие переменные экземпляра должны участвовать в определении равенства. Например, равенство двух рядов определяется при помощи проверки их размеров и затем проверки равенства каждого элемента массивов; равенство двух чисел определяется проверкой представляют ли числа одно и то же значение; равенство двух счетов в банке может быть проверено только проверкой равенства идентификационного номера.

Сообщение хэш это специальная часть протокола сравнения. Ответ на хэш это целое. Любые два равные объекта должны возвращать одно и то же значение хэша. Неравные объекты могут либо не могут возвращать равные значения хэша. Обычно это целое значение используется как номер для сохранения объекта в нумерованной совокупности (как показано в главе 3). Всегда когда переопределяется сообщение = также должно быть переопределено сообщение хэш для сохранения свойства равенства хэшей у равных объектов.

Протокол экземпляров Объекта
сравнение
== объект отвечает являются ли получаетль и аргумент одним и тем же объектом.
= объект отвечает представляют ли поулчаетль и аргумент одинаковые компоненты.
~= объект отвечает представляют ли получаетль и аргумент разные компоненты.
~~ объект отвечает являются ли получаетль и аргумент разными объектами.
хэш возвращает целое вычисленное на основе значения получателя.

Реализация по умолчанию сообщения = та же что и для сообщения ==.

Некоторые специализированные протокол сравнения предоставляют краткий способ проверить идентичность с объектом пусто.

Протокол экземпляров Объекта
проверки
это пусто отвечает является ли получатель нулём.
не пусто отвечает является ли получаетль отличным от нуля.

Эти сообщений соответветственно идентичны следующим предложениям == пусто и ~~ пусто. Выбор используемого способа зависит от персонального стиля программирования.

Некоторые очевидные примеры

предложение значение
пусто это пусто. истина
истина не пусто. истина
3 это пусто. ложь
#( ) = #( ). истина
3 = (6 / 2). истина
#( 1 2 3 ) класс == Ряд. истина

Копирование объектов[]

Есть два способа сделать копию объекта. Разница заключается в ток копируются или нет переменные объекта. Если переменные не копируются, то они разделяются (поверхностная копия); если значения переменных копируются, то они не разделяются между объектами (глубокая копия).

Протокол экземпляров Объекта
копирование
копия возвращает другой экземпляр такой же как получатель.
поверхностная копия возвращает копию получателя которая разделяет переменные экземпляра с исходным объектом.
глубокая копия возвращает копию получателя со своей собственной копией переменных экземпляра.

Реализация по умолчанию для метода копия это поверхностная копия. Метод копия обычно переопределяется в подклассах где результатом копирования является комбинация разделяемых и не разделяемых переменных, в отличии от методов поверхностная копия и глубокая копия.

Например копия (поверхностная копия) Ряда ссылается на те же элементы что и исходный Ряд, но копия это другой объект. Замена элемента в копии не изменяет исходного объекта.

предожение результат
а#( 'первый' 'второй' 'третий' ). ('первый' 'второй' 'третий')
ба копия. ('первый' 'второй' 'третий')
а = б. истина
а == б. ложь
(а от: 1) == (б от: 1). истина
б от: 1 пом: 'новый первый'. 'новый первый'
а = б. ложь
а'привет'. 'привет'
ба копия. 'привет'
а = б. истина
а == б. ложь

Рисунок 6.1. показывает взаимоотношение между поверхностной и глубокой копией. Рассмотрим пример Записи о человеке, для дальнейшей иллюстрации разницы между поверхностной копией и глубокой копией. Допустим в определении записи указана переменная страховка, экземпляр класса Страховка. Предположим также что каждый экземпляр Страховки имеет значение связанное с объёмом медицинского обслуживания. Предположим что мы создали запись о служащем как экземпляр прототип Записи о человеке. Под словом прототип мы подразумеваем что объект имеет все значения как у любого нового экземпляра класса, поэтому этот экземпляр может быть создан просто копированием а не посылкой последовательности инициализирующих сообщений. Предположим также что этот экземпляр прототип является переменной класса Запись о человеке и что ответ на создание новой Записи о человеке это поверхностная копия этой переменной; поэтому метод связанный с сообщением новый это ↑ запись о служащем копия.

6

Рисунок 6.1

В результате выполнения предложения

запись Иванов ИванЗапись о человеке новый.

запись Иванов Иван ссылается на копию (в действительности на поверхностную копию) записи о служащем.

Прототип запись о служащем и запись Иванов Иван разделяют одну и ту же страховку. Политика компании может измениться. Допустим Запись о человеке понимает сообщение изменить предел страховки: число, которое реализуется при помощи изменения предела страховки записи о служащем. Т.к. эта страховка разделяется, то результатом выполнения предложения

Запись о человеке изменить предел страховки: 4000.

будет изменение объёма медицинских услуг для всех работников. В примере изменится и страховка записи о служащем и страховка записи Иванов Иван. Сообщение изменить предел страховки: посылается классу Запись о человеке т.к. это подходящий объект для сбора сведений об изменении всех экземпляров.

Доступ к частям объекта[]

В системе Смолток есть два вида объектов, объекты с именованными переменными и объекты с нумерованными переменными. Объекты с нумерованными переменными могут также иметь именованные переменные. Это различие было рассмотрено в Главе 3. Класс Объект поддерживает шесть сообщений предназначенных для доступа к нумерованным переменным объекта. Вот эти сообщения

Протокол экземпляров Объекта
доступ
от: номер возвращает значение нумерованной переменной экземпляра получателя чей номер это аргумент, номер. Если получатель не имеет нумерованных переменных, или аргумент больше чем количество нумерованных переменных то выдаётся сообщение об ошибке.
от: номер пом: объект помещает аргумент, объект, как значение нумерованной переменной экземпляра получателя чей номер это аргумент, номер. Если получатель не имеет нумерованных переменых, то выдаётся сообщение об ошибке. Возвращает объект.
основной от: номер то же что и от: номер. Однако метод связанный с этим сообщением не может быть изменён в любом подклассе.
основной от: номер пом: объект то же что и от: номер пом: объект. Однако метод связанный с этим сообщением не может быть изменён в любом подклассе.
размер возвращает количество доступных нумерованных переменных. Это то же самое что и наибольший допустимый номер.
основной размер то же что и размер. Однако метод связанный с этим сообщением не может быть изменён в любом подклассе.

Заметьте что метод доступа идут парами, одно сообщение в каждой паре начинается со слова основной означающим что это базисное сообщение системы чья реализация не должна изменяться в любом подклассе. Причина предоставления этих пар сообщений в том что внешний протокол; от:, от:пом: и размер; может быть переопределён для специальных случаев, в то же время остаётся способ получить доступ к примитивным методам. (в Глава 4 приведено объяснение примитивных методов, это методы которые реализуются для системы виртуальной машиной.) Поэтому в любом методе ирархии описания класса сообщение основной от:, осовной от:пом: и основной размер должны всегда использоваться для доступа к примитивным методам. Сообщение основной размер может быть послано любому объекту; если объект не переменной длины, то ответом будет 0.

Экземпляры класса Ряд это объекты с переменной длиной. Допустим буквы это Ряд #($а $б $в $г $д ), тогда

предложение результат
буквы размер. 5
буквы от: 3.
буквы от: 3 пом: .
буквы. #($а $б $е $г $д )

Печать и сохранение объектов[]

Есть много способов создать последовательность знаков которая предоставляет описание объекта. Описание может быть только ключём например для отличия объекта, или описание может быть достаточно подробным чтобы можно было создать подобный объект. В первом случае (печать), описание может быть хорошо форматировано, например как результат функции Lisp pretty-printing. Во втором случае (сохранение), описание может сохранять информацию разделяемую с другими объектами.

Протокол сообщений классов системы Смолток поддерживает печать и сохранение. Реализация этих сообщений в классе Объект предоставляет минимальные возможности; большинство классов переопределяют сообщения чтобы улучшить создаваемые описания. Аргументы двух сообщений это экземпляры разновидности Потока; Потоки описаны в главе 12.

Протокол экземпляров Объекта
печать
цепь для печати возвращает Цепь чьи знаки описывают получателя.
печать в: поток добавляет к аргументу, поток, цепь чьи знаки описывают получателя.
сохранение
цепь для сохранения возвращает цепь предствляющую получателя из которой он может быть воссоздан.
сохранить в: поток добавляет к аргументу, поток, цепь представляющую получателя из которой он может быть воссоздан.

Каждый из двух видов печати основан на создании последовательности знаков которые могут быть показаны на мониторе, записаны в файл или переданы по сети. Последовательность созданная сообщениями цепь для сохранения или сохранить в: должна быть проинтерпретирована как одно или более предложений которые могут быть выполнены чтобы воссоздать объект. Например Множество из трёх элементов $а $б $в может быть напечатано как

Множество ( $а $б $в ).

в то время как оно может быть сохранено как

(Множество новый добавить: $а; добавить: $б; добавить: $в.)

Литералы могут использовать одно и то же представление для печати и для сохранения. Поэтому цепь 'привет' должна печататься и сохраняться как 'привет'. Символ #имя печатается имя, но сохраняется как #имя.

Для сохранения наибольшей информации реализация по умолчанию для цепи для печати это имя класса объекта; реализация по умолчанию для цепь для сохранения это имя класса со следующим сообщением создания экземпляра основной новый и последующая последовательность сообщений для сохранения переменных экземпляра. Например если подкласс Объекта, скажем класс Пример, демонстрирует поведение по умолчанию, тогда для экземпляра Примера с именем прим у которого нет переменных экземпляра, мы должны получить

предложение результат
прим цепь для печати. 'Пример'
прим цепь для сохранения. '(Пример основной новый)'

Обработка ошибок[]

Тот факт что все действия выполняются при помощи посылки сообщений объекта означает что существует только одна причина ошибки которая должна обрабатываться системой: сообщение послано объекту, но сообщение не определено ни в одном классе цепи наследования. Эта ошибка обнаруживается интерпретатором чьей реакцией является посылка исходному объекту сообщения не понимаю: сообщение. Аргумент, сообщение, представляет селектор неудачного сообщения и связанные с ним аргументы, если они есть. Метод связанный с селектором не понимаю: выдаёт для пользвателя сообщение что произошка ошибка. Способ представления этого сообщения пользователю является функцией (графического) интерфейса поддерживаемая системой и не описывается здесь; минимальным требованием интерактивной системы является возможность напечатать сообщение об ошибке на устройсто вывода и затем дать пользователю возможность исправить ошибочную ситуацию. Глава 17 описывает сообщение об ошибке системы Смолток и методы отладки.

В дополнение к основному сообщению об ошибке, методы могут явно использовать механизм обработки ошибок системы для случаев в которых проверка определяет что программа совершает что то недопустимое. В таком случае метод может задать описание ошибке которое будет показано пользователю. Обычный способ сделать это - послать текущему экземпляру сообщение ошибка: цепь, где аргумент представляет желаемый комментарий. Реализация по умолчанию для этого сообщения - вызов механизма оповещения системы. Программист может предоставить альтернативную реализацию для ошибки: которая использует зависящее от программы сообщение об ошибке.

Общие сообщения поддерживаются в протоколе класса Объект. Сообщение об ошибке может указывать что примитив системы не выполнился или что подкласс переопределил унаследованное сообщение которое не поддерживается и что пользователь не должен его вызывать или что суперкласс определяет сообщения которые должны быть реализованы в подклассах.

Протокол экземпляров Объекта
обработка ошибок
не понимаю: сообщение сообщает пользователю что получатель не понимает аргумента, сообщение, в качестве сообщения.
ошибка: цепь сообщает пользователю что произошла ошибка при ответе на сообщение посланное получателю. Сообщение использует аргумент, цепь, как часть уведомления об ошибке.
примитив не выполнен сообщает пользователю что метод реализованый как примитив системы не смог правильно завершиться.
не должен реализовывать сообщает пользователю что не смотря на то что суперкласс получаетеля определяет это сообщение оно не должно быть реализовано в подклассе, класс получателя не может предоставить подходящей реализации.
ответственность подкласса сообщает пользователью что метод определённый в суперклассе получателя должен быть реализован в классе получателя.

Подклассы могут переопределять сообщения обработки ошибок чтобы предоставить специальную поддержку для соответствующих ошибочных ситуаций. Глава 13 которая описывает реализацию классов наборов даёт примеры использования последних двух сообщений.

Advertisement