forked from RussianSmalltalk/pharo-by-example-ru
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ch05.tex
387 lines (266 loc) · 27.6 KB
/
ch05.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
\documentclass[a4paper,10pt,twoside]{book}
%=============================================================
\input{common.tex}
\graphicspath{{figures/}} % for \dothis
%=============================================================
\begin{document}
\sloppy
\mainmatter
%=============================================================
\setcounter{chapter}{4} % и тогда следующая глава будет 5
\chapter{}
%=============================================================
Модель программирования Smalltalk проста и единообразна: всё~--- объекты, и объекты взаимодействуют только через посылку сообщений. Однако, эти простота и единообразность могут быть источником проблем для программистов, использующих другие языки. В этой главе мы представляем основные понятия модели объекта Smslltalk; в частности мы обсудим последствия представления классов как объектов.
\section{Правила моделей (модели)}
Объектная модель в Smalltalk базируется на множестве простых правил, которые применяются однообразно. Правила следующие:
\begin{enumerate}[label={\textbf{Rule \arabic{*}}.}, ref={Rule \arabic{*}}, leftmargin=*]
\item[Правило 1.]{}
Все~--- это объект..
\item[Правило 2.]{}
Каждый объект~--- это экземпляр класса.
\item[Правило 3.]{}
У каждого класса есть суперкласс.
\item[Правило 4.]{}
Все операции происходят, посылая сообщения.
\item[Правило 5.]{}
Метод поиска следует за цепью наследования.
\end{enumerate}
Давайте рассмотрим каждое из этих правил в деталях
\section{Все~--- объекты}
Правило <<все~--- объекты>>~--- основное. Уже после небольшого периода работы с языком Smalltalk, вы начнете удивляться тому, как это правило упрощает все, что вы делаете. Целые числа, например, действительно объекты, так что вы можете посылать им сообщение, как и любому другому объекту.
\begin{code}{}
3 + 4 --> 7 "сообщение '+ 4' адресовано 3, результат 7"
20 factorial --> 2432902008176640000 "сообщение factorial, в результате получаем огромное число"
\end{code}
Представление 20 factorial конечно отличается от представления 7, а потому что они оба объекты, никакой кусок кода~--- и даже не реализация факториала~--- не должна знать об этом.
Пожалуй, наиболее фундаментальное следствие этого правила состоит в следующем:
\important{Классы тоже объекты!}
Кроме того, классы не являются объектами второго класса: они действительно являются первым классом объектов, которым вы можете отправлять сообщения, проверять и так далее. Это означает, что Pharo~--- это по-настоящему мыслящая система, дающая большие возможности разработчикам.
Углубимся в реализацию Smalltalk, тут различают три различных вида объектов. Есть (1) обычные объекты с переменными экземпляра, которые передаются ссылками, eсть (2) небольшие целые числа (small integers), которые передаются по значению, и (3) индексируемые объекты, такие как массивы, которые держат непрерывную часть памяти. Красота Smalltalk в том, что вам не нужно заботиться о различиях между этими тремя видами объектов.
\section{Каждый объект~--- экземпляр класса}
Каждый объект принадлежит классу; вы можете узнать к какому именно, послав ему (объекту) сообщение class.
\begin{code}{}
1 class --> SmallInteger
20 factorial class --> LargePositiveInteger
'hello' class --> ByteString
#(1 2 3) class --> Array
(4@5) class --> Point
Object new class --> Object
\end{code}
Класс определяет структуру его экземпляров через переменные экземпляра, и поведение его экземпляров с помощью методов. Каждый метод имеет имя, назовём его селектор, который является уникальным в пределах класса.
Поскольку классы являются объектами, и каждый объект является экземпляром класса, отсюда следует, что классы должны быть экземплярами классов. Класс, экземпляры которого являются классами называется Метакласс. Всякий раз когда вы создаёте класс, система автоматически создаёт и метакласс за вас. Метакласс определяет структуру и поведение класса, который является его экземпляром. 99\% времени, вам не придется думать о метаклассах, и, возможно, с радостью их игнорировать. (Более подробно метаклассы будут рассмотрены в главе 13.)
\subsection{Переменные экземпляра}
Переменные экземпляра в Smalltalk являются частными относительно экземпляра как такового. В этом заключается отличие от Java и C++, которые позволяют переменным экземпляра (также известным как <<поля>>, или <<переменные-члены>>) быть доступными любому другому экземпляру этого же класса. Мы говорим, что граница инкапсуляции объектов в Java и C++ является класс, тогда как в Smalltalk это экземпляр.
В Smalltalk, два экземпляра одного и того же класса не могут получить доступ к переменным экземпляра друг друга, пока класс определяет <<методы доступа>>. Нет такого синтаксиса,который обеспечивает прямой доступ к переменным экземпляра любого другого объекта. (На самом деле, механизм, называемый отражением, действительно дает способ спрашивать у других объектов значения его переменных экземпляра; мета программирование предназначено для написания инструментов,такие как контроллер объектов,единственной целью которых является просмотр изнутри других объектов.)
Переменные экземпляра могут быть доступны по имени в любом методе экземпляра того класса,который определяет их, а так же в методах определенных в своих подклассах. Это значит,что переменные экземпляра в smalltalk идентичны защищенным переменным (protected) в С++ и Java. Однако, мы предпочитаем говорить, что они являются частными, потому что считается плохим стилем в Smalltalk, получать доступ к переменной экземпляра непосредственно из подкласса.
\subsection{Пример}
Метод Point>>dist: (метод 5.1) вычисляет расстояние между двумя точками(между получателем и другой точкой. Переменные экземпляра х и у получателя напрямую доступны через тело метода. Как бы то ни было, переменные экземпляра другой точки должны быть доступны через посылку им сообщений х и у.
\begin{method}{Расстояние между двумя точками}
Point>>>dist: aPoint
"Найти расстояние между двумя точка и выдать".
| dx dy |
dx := aPoint x - x.
dy := aPoint y - y.
^ ((dx * dx) + (dy * dy)) sqrt
\end{method}
\begin{code}{}
1@1 dist: 4@5 --> 5.0
\end{code}
Основной причиной, по которой предпочитают инкапсуляцию на основе экземпляров инкапсуляции на основе классов является то, что она позволяет различным реализации одной и той же абстракции сосуществовать. Например, метод Point>>dist:, не знает и не заботится о том, является ли аргумент aPoint экземпляром того же класса, что и получатель.
Аргумент объекта может быть представлен в полярных координатах, или как запись в базе данных, или на другом компьютере в распределенной системе; до тех пор, {\color{red}(пока он может отвечать на сообщения х и у, код в методе 5.1 будет по-прежнему работать)} пока он может реагировать на сообщения х и у, код в метод 5.1 по-прежнему будет работать.
\subsection{Методы}
Все методы открыты. Методы сгруппированы в протоколы, которые указывают на их цель. Некоторые общие названия протокола были созданы в соответствии с соглашением, например accessing для всех методов доступа и initialization для создания согласующегося начального состояния объекта. Протокол private иногда используется для группы методов, которые не следует рассматривать извне. Ничто, однако, не запрещает вам отправлять сообщения, которые реализуется таким <<private>> методом.
Методы могут получить доступ ко всем переменным экземпляра объекта. Некоторые смолтокеры предпочитают получать доступ к переменным класса только через методы доступа. Эта практика имеет некоторую ценность, но и загромождает интерфейс вашего класса, и что хуже, выставляет private состояние всему.
\subsection{Стороны экземпляра и стороны класса}
Поскольку классы являются объектами, то они могут иметь свои переменные экземпляра и свои методы. Мы называем это переменными экземпляра класса и методами класса, но они ничем не отличаются от обычных переменных экземпляра и методов: переменные экземпляра класса~--- только переменные экземпляра, определенные метаклассом, а методы класса~--- только методы, определенные метаклассом.
Класс и метакласс~--- это два отдельных класса, хотя первый является экземпляром последнего. Однако, это не играет большой роли для вас, как программистов; вы обеспокоены определением поведения ваших объектов и классов, которые создают их.
По этой причине, браузер помогает вам просматривать как класс, так и метакласс, как если бы они были медалью с двумя "сторонами": "сторона экземпляра" и "сторона класса" , так как показано на рисунке 5.1 Нажатие на кнопку \button{instance} например просматривает класс Color, то есть, вы увидите методы, которые выполняются когда сообщения отправлены к экземпляпу Color, например синий цвет. Нажатие на кнопку \button{class} просматривает класс Color class, то есть вы видите методы, которые будут выполняться, когда сообщения отправлены к классу Color как таковому). Например, Color blue посылает сообщение blue к классу Color. Поэтому вы найдёте метод blue, определённый на стороне класс, а не на стороне экземпляр.
\begin{code}{}
aColor := Color blue. "Class side method blue"
aColor --> Color blue
aColor red --> 0.0 "Instance side accessor method red"
aColor blue --> 1.0 "Instance side accessor method blue"
\end{code}
Вы определяете класс, заполняя шаблон, предложенный со стороны экземпляра. Когда вы принимаете этот шаблон, система создает не только класс, который вы определили, но и соответствующие метаклассы. Вы можете просматривать метакласс, нажав на кнопку \button{class}. Только часть шаблона создания метакласса, что имеет смысл для редактирования непосредственно,~--- список имен переменных экземпляра.
Как только класс был создан,нажав на кнопку \button{instance} можно редактировать и просматривать методы, которыми будут обладать экземпляры этого класса (и его подклассов). Например, мы можем видеть на рисунке 5.1, что метод hue определён на экземплярах класса Color. А кнопка \button{сlass} позволяет вам просматривать и редактировать метакласс (в этом случае Color class).
\subsection{Методы класса}
Методы класса могут быть весьма полезны: рассмотрим Color class в качестве примера. Вы увидите, что есть два вида методов, определенных в классе: те, которые создают экземпляры класса, типа Color class>>blue и те, которые выполняют полезные функции, типа Color class>>showColorCube. Это типичный пример использования методов, хотя иногда встречаются методы, используемые в других отношениях.
Удобно размещать служебные методы на стороне класса, потому что они могут быть использованы без создания дополнительных объектов. Действительно, многие из них будут содержать комментарии, разработанные для того чтобы облегчить выполнение их.
\dothis{Для просмотра метода Color>>showColorCube, дважды щелкните на комментарий <<Color showColorCube>> и введите CMD-d.}
Вы увидите эффект выполнения этого метода. (выберите \menu{World. \go Restor display (r)}, чтобы отменить эффект.)
Для тех, кто знаком с Java и C++, методы класса могут показаться похожими на статические методы. Тем не менее, единообразность Smalltalk означает, что они несколько другие: в то время как в Java статические методы на самом деле просто статически разрешенные процедуры, в классах Smalltalk методы динамически направленные методы. Это означает, что наследование, переопределение и суперпозиция работают в методах класса в Smalltalk, в то время как они не работают для статических методов в Java.
\subsection{Переменные экземпляра класса}
С обычными переменными, например, все экземпляры класса имеют одинаковый набор имен переменных, и его подклассы наследуют эти имена, однако, каждый экземпляр имеет свой собственный набор значений. То же самое с переменными экземпляра класса: каждый класс имеет свои собственные частные переменные экземпляра класса. Подкласс унаследует эти переменные экземпляра класса, но он имеет свои собственные копии этих переменных. Так же, как объекты не разделяют переменные экземпляра, так и классы и их подклассы не разделяют переменные экземпляра класса.
Вы можете использовать переменную экземпляра класса, называемую count для того, чтобы отслеживать как много экземпляров создали данного класса. Тем не менее, любой подкласс будет иметь свою собственную переменную count, таким образом экземпляры подклассов будут считаться отдельно.
Пример: переменные экземпляра класса не являются общими с подклассом. Предположим, что мы определили классы Dog и Hyena, где Hyena наследует переменную экземпляра класса count от Dog.
\begin{classdef}{Dogs и Hyenas}
Object subclass: #Dog
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'PBE-CIV'
Dog class
instanceVariableNames: 'count'
Dog subclass: #Hyena
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'PBE-CIV'
\end{classdef}
Теперь предположим, что мы определили методы для класса Dog, инициализируя его count нулём и увеличивать его когда создадутся новые экземпляры:
\begin{method}{Keeping count of new dogs}
Dog class>>>initialize
super initialize.
count := 0.
Dog class>>>new
count := count +1.
^ super new
Dog class>>>count
^ count
\end{method}
Теперь когда мы создали новый Dog его счётчик увеличился, и такое же происходит для каждого Hyena, но они считаются отдельно:
\begin{code}
Dog initialize.
Hyena initialize.
Dog count --> 0
Hyena count --> 0
Dog new.
Dog count --> 1
Dog new.
Dog count --> 2
Hyena new.
Hyena count --> 1
\end{code}
Отметим также, что переменные экземпляра класса являются закрытыми для класса точно так же, как переменные экземпляра являются закрытыми для экземпляра. Так как классы и их экземпляры -различные объекты, отсюда следует:
\important{Класс не имеет доступа к переменным экземпляра своего собственного экземпляра.}
\important{Экземпляр класса не имеют доступа к переменным экземпляра класса своего собственного класса.}
По этой причине, методы инициализации экземпляра всегда должна быть определены на стороне экземпляра- сторона класса не имеет доступа к переменным экземпляра, поэтому не может инициализировать их! Все, что класс может сделать, это отправить инициализации сообщения, возможно, используя методы доступа, на вновь созданные экземпляры.
\\
Точно также, экземпляры могут иметь доступ к переменным экземпляра класса косвенно, с помощью отправки сообщений доступа к своему классу.
В Java нет эквивалента переменным экземпляра класса. Статические переменные Java и C++ больше похожи на переменные класса Smalltalk, которые мы обсудим в разделе 5.7: все подклассы и все их экземпляры имеют одни и те же статические переменные.
\paragraph{Пример: Определение Singleton.} Singleton pattern\footnote{Sherman R. Alpert, Kyle Brown and BobbyWoolf, The Design Patterns Smalltalk Companion.
Addison Wesley, 1998, ISBN 0–201–18462–1.} является типичным примером использования переменных экземпляра класса и методов класса. Представьте себе, что мы хотели бы реализовать класс WebServer и использовать Singleton pattern, чтобы убедиться, что он имеет только один экземпляр.
Нажатие на кнопку \button{instance} в браузере, мы определяем класс WebServer следующим образом (класса 5.4).
\begin{classdef}{Одноэлементный класс}
Object subclass: #WebServer
instanceVariableNames: 'sessions'
classVariableNames: ''
poolDictionaries: ''
category: 'Web'
\end{classdef}
Затем, нажав на кнопку \button{class}, мы добавим переменную экземпляра uniqueInstance на сторону класса.
\begin{classdef}{сторона класса единичного класса}
WebServer class
instanceVariableNames: 'uniqueInstance'
\end{classdef}
Следствием этого является то, {\color{red}(что у класса WebServer теперь есть ещё одна переменная экземпляра, в дополнение к переменным, которые он наследует, такие как superclass и methodDict)} что класс WebServer теперь есть еще один экземпляр переменной, помимо переменных, которые он наследует, таких, как суперкласс и methodDict.
Теперь мы можем определить метод класса по имени uniqueInstance, как показано в методе 5.6. Этот метод сначала проверяет, является ли uniqueInstance инициализированным. Если это не так, метод создает экземпляр и присваивает его переменной экземпляра класса uniqueInstance. Наконец, значение uniqueInstance возвращается. C тех пор, как uniqueInstance является переменной эеземпляра класса, этот метод может иметь непосредственно к нему доступ.
\begin{method}{uniqueInstance (на стороне класса)}
WebServer class"uniqueInstance
uniqueInstance ifNil: [uniqueInstance := self new].
^ uniqueInstance
\end{method}
В первый раз, когда WebServer uniqueInstance выполнен, экземпляр класса WebServer будет создан и присвоен переменной uniqueInstance.В следующий раз, ранее созданный экземпляр будет возвращен, а не создан новый.
Обратите внимание, что код создания экземпляра внутри условного метода в 5,6 написан как self new, а не как WebServer new. В чем разница? Как только метод uniqueInstance определён в WebServer class, вы можете подумать, что они были одинаковыми. И действительно, пока кто-то не создает подкласс Web-Server, они~--- одно и то же. Но предположим, что ReliableWebServer является подклассом WebServer, и наследует метод uniqueInstance. Мы, несомненно, ожидаем, что ReliableWebServer uniqueInstance ответит ReliableWebServer: использование self гарантирует, что это произойдет, так как он будет привязан к соответствующему классу. Отметим также, что WebServer и ReliableWebServer будут иметь каждый свои переменные экземпляра класса по именем uniqueInstance. Эти две переменные, конечно, будут иметь различные значения.
\section{Каждый класс имеет суперкласс}
Каждый класс в Smalltalk наследует своё поведение и описание его структуры от отдого суперкласса. Это означает, что Smalltalk имеет единичное наследование.
\begin{code}{}
SmallInteger superclass --> Integer
Integer superclass --> Number
Number superclass --> Magnitude
Magnitude superclass --> Object
Object superclass --> ProtoObject
ProtoObject superclass --> Nil
\end{code}
Традиционно корень иерархии наследования Smalltalk есть класс Object (поскольку всё является объектом). В Pharo, корень на самом деле класс ProtoObject, но вы, как правило, не обращайте никакого внимания на этот класс. ProtoObject инкапсулирует минимальный набор сообщений, которые все объекты должны иметь. Тем не менее, большинство классов наследуются от Object , который определяет множество дополнительных сообщений, что почти все объекты должны понимать и реагировать на них. Если у вас есть очень веские причины поступить иначе, при создании приложений классов, вам следует нормализовать подкласс Object, или один из его подклассов.
\dothis{Новый класс обычно создается, отправив сообщение subclass: instanceVariableNames: ... к существующему классу. Есть несколько других методов для создания классов. Взгляните на протокол \menu{Kernel-Classes \go Class \go subclass сreation}, чтобы увидеть, какие есть методы создания.}
Хотя Pharo не обеспечивает множественное наследование, он поддерживает механизм, называемый traits для совместной деятельности поведения между несвязанными классами. Traits это набор методов, которые могут быть повторно использованы несколькими классами, которые не связаны наследованием. Использование traits позволяет совместно использовать код между различными классами без дублирования кода.
\subsection{Абстрактные методы и абстрактные классы}
Абстрактный класс~--- это класс, который существует для того, чтобы быть разделенным на подклассы, а не быть экземпляром. Абстрактный класс, как правило, неполный, в том смысле, что он не определяет все методы, которые он использует. <<Missing>> методы~--- это те методы, которые предполагают другие методы, но которые сами не являются определёнными, это и есть абстрактные методы.
Smalltalk не имеет специального синтаксиса для указания того, что метод или класс является абстрактным. По соглашению, тело абстрактного метода состоит из собственных выражений subclassResponsibility. Они известны как <<marker method>>, и указывают, что подклассы {\color{red}(несут ответственность за определение)} имеют ответственность за определение конкретных вариантов метода. SelfsubclassResponsibility методы всегда должны быть переопределены, и поэтому никогда не должны быть исполнены. Если вы забудете переопределить его, и он будет выполнен, будет вызвано исключение.
Класс считается абстрактным, если один из его методов является абстрактным. Ничего на самом деле ни мешает вам создать экземпляр абстрактного класса, все будет работать, пока абстрактный метод не вызовется.
\subsubsection{Пример: класс Magnitude.}
Magnitude представляет собой абстрактный класс, который помогает нам определить объекты, которые можно сравнивать друг с другом. Подклассы Magnitude должны реализовывать методы <, = и {\color{red}(???????!!!!!!!!!!)} хэш. Использование таких сообщений Magnitude определяет другие методы, такие как >, >=, <=, max:, min: between:and: и другие для сравнения объектов. Такие методы наследуются подклассами. Метод < является абстрактным и определен, как показано в методе 5.7.
\begin{method}{Класс Magnitude>> <}
Magnitude>>> < aMagnitude
"Answer whether the receiver is less than the argument."
^self subclassResponsibility
\end{method}
В отличие от этого метода >= является конкретным, он определяется в терминах <:
\begin{method}{Magnitude>> >=}
>= aMagnitude
"Answer whether the receiver is greater than or equal to the argument."
^(self < aMagnitude) not
\end{method}
То же самое относится и к другим методам сравнения.
Character подкласса Magnitude, она переопределяет метод subclassResponsibility для <с его собственной версией < (см. метод 5.9). Character также определяет методы hash и =; он наследует от Magnitude методы> =, <=, = и другие.
\begin{method}{Character>> <}
Character>>> < aCharacter
"Answer true if the receiver's value < aCharacter's value."
^ self asciiValue < aCharacter asciiValue
\end{method}
\subsection{Трэйты}
Трэйт~--- набор методов, которые могут быть включены в поведение класса без необходимости наследования. В результате для класса становится легко иметь уникальный суперкласс, при этом продолжая делиться полезными методами с другими несвязанными классами.
Чтобы определить новый трэйт, просто замените шаблон создания подкласса на сообщение к классу Trait.
\begin{classdef}{Определение нового трэйта}
Trait named: #TAuthor
uses: { }
category: 'PBE-LightsOut'
\end{classdef}
Здесь мы определяем трэйт TAuthor в категории РВЕ-LightsOut. Этот трэйт не использует никаких других существующих трэйтов. В общем мы можем указать строение трэйт-выражения других трэйтов, чтобы использовать как часть uses: дескриптор аргумента. В примере мы просто указали пустой массив.
Трэйты могут содержать методы, но не переменные экземпляра. Предположим, что мы хотели бы иметь возможность добавлять author-метод в различные классы, независимо от того, какое место они занимают в иерархии. Это можно сделать следующим образом:
\begin{method}[doit]{Метод author}
TAuthor >>> author
"Returns author initials"
^ 'on' "oscar nierstrasz"
\end{method}
Теперь мы можем использовать этот трэйт в классе, который уже имеет свой суперкласс, для экземпляра LOGame это класс, который мы определили во второй главе. Мы просто изменим шаблон создания класса для LOGame включив uses: дескриптор аргумента, который определяет, что следует использовать TAuthor.
\begin{classdef}{Использование нового трэйта}
BorderedMorph subclass: #LOGame
uses: TAuthor
instanceVariableNames: 'cells'
classVariableNames: ''
poolDictionaries: ''
category: 'PBE-LightsOut'
\end{classdef}
Если мы теперь сделаем экземпляр LOGame, то он будет реагировать на author сообщения,как и предполагалось.
\begin{code}{}
LOGame new author --> 'on'
\end{code}
Структура трэйт-выражения может объединять множество трэйтов используя оперетор +. В случае конфликтов (т.е. если множество трэйтов определяет методы с одинаковыми именами), эти конфликты могут быть разрешены явным удалением этих методов (с помощью -), или же переопределение этих методов в классе или в трэйте, который вы определяете. Так же возможно создание alias-методов, или псевдонимов, (с помощью \@), тем самым обеспечивая новое имя для них. Трэйты используются в systemkernel (ядре системы). Хорошим примером является класс Behavior.
\begin{classdef}{Определение Behavoior используя трэйты}
Object subclass: #Behavior
uses: TPureBehavior @ {#basicAddTraitSelector:withMethod:->
#addTraitSelector:withMethod:}
instanceVariableNames: 'superclass methodDict format'
classVariableNames: 'ObsoleteSubclasses'
poolDictionaries: ''
category: 'Kernel-Classes'
\end{classdef}
Здесь мы видим, что метод addTraitSelector: withMethod: определен в трэйте TPureBehavior, имеющий псевдоним basicAddTraitSelector: withMethod. Поддержка трэйтов в настоящее время добавлена в браузер.
\section{Все происходит посредством посылки сообщений}
Это правило отражает суть программирования на Smalltalk.
В процедурном программировании, выбор того, какая часть кода выполняется при вызове процедуры происходит посредством caller. Caller выбирает какую процедуру или функцию исполнить статично, по имени.
В объектно-ориентированном программировании,мы не "вызываем методы", мы "посылаем сообщения". Выбор терминологии при этом принципиален. У каждого объекта есть своя функциональность, за что он ответственен. Мы не говорим объекту что делать, применяя к нему некую процедуру. Вместо этого мы вежливо просим объект сделать что-либо для нас, посредством посылки сообщения. При этом сообщение это не часть кода: это ничто иное как имя и список аргументов. Получатель решает как ответить, выбирая свой собственный метод для исполнения того, что было запрошено. Ровно с тех пор, как у различных объектов могут быть различные методы для ответа на одно и то же сообщение, метод должен динамически выбираться, когда это сообщение получено.
\begin{code}{}
3 + 4 --> 7 "send message + with argument 4 to integer 3"
(1@2) + 4 --> 5@6 "send message + with argument 4 to point (1@2)"
\end{code}
И как следствие, мы можем посылать одни и те же сообщения различным объектам, каждый из которых может иметь свой собственный метод для ответа на такого рода сообщение. Мы не говорим SmallInteger 3 или Point 1\@2 как ответить на сообщение +4. у каждого есть свой метод для +, и ответ на +4 соответственно.
Одним из следствий модели посылки сообщений в Smalltalk является то, что он поддерживает стиль, в котором объекты как правило, имеют крайне необъемные методы и передают задачи другим объектам, а не реализующие огромные процедурные методы, которые предполагают и большую ответственность. Джозеф Пелрайн (Joseph Pelrine) выразил этот принцип кратко следующим образом:
\important{Не делайте ничего, что может быть сделано другим.}
Многие объектно-ориентированные языки поддерживают как статические, так и динамические операции для объектов; в Smalltalk поддерживается только динамические сообщения. Вместо предоставления статическому классу операции, для экземпляров, классы~--- это объекты и мы просто посылаем сообщения классам.
Почти все в Smalltalk происходит посредством посылки сообщений. В некоторой активной точке должно происходить следующее:
\begin{itemize}
\item
Объявления переменных это не посылка сообщения. На самом деле, объявления переменных даже не исполняемы. Объявление переменной просто занимает место для ссылки на объект.
\item
Присваивания не посылают сообщений. Присваивания переменным влечет то, что имя переменной будет связано в рамках его определения.
\item
Ретерны (returns) не посылают сообщения. Ретерн просто заставляет вычисленный результат вернуть отправителю.
\item
Примитивы не посылают сообщения. Они реализованы в виртуальной машине.
\end{itemize}
Кроме этих нескольких исключений, почти все другое действительно происходит посредством посылки сообщений. В частности, с тех пор, как нет "открытых полей" (public fields) в Smalltalk, единственным путем обновлять переменные экземпляра другого объекта это посылка сообщения, в котором просят обновить его поле. Конечно, обеспечение setter и getter методов для всех переменных экземпляра объекта это плохой объектно-ориентированный стиль. Джозеф Пелрайн (Joseph Pelrine) также утверждает:
\important{Не позволяйте кому-нибудь еще играть с вашими данными}
\end{document}