Smalltalk в примерах/Создание экземпляра

Сейчас мы рассмотрим более подробно как создавать экземпляры класса. Основное сообщение для создания экземпляра это \emph{новый}, которое возвращает новый экземпляр класса. Например,


служащий := Служащий новый.
совокупность := УпорядоченнаяСовокупность новый.

Если ты пошлёшь классу совокупность сообщение \emph{новый}, оно вернёт новую совокупность\footnote{Sending new: to a class that is not variable sized will generate an exception}. Размер всегда должен быть нулевым пока совокупность пустая, но вместимость будет равна вместимости по умолчанию для данного класса. Вместимость это количество элементов которые ты можешь поместить в совокупность до того как он начнёт увеличиваться. Например, выполнение следующих выражений даёт вместимость показанную справа.


\begin{verbatim} --- \end{verbatim}

Увеличение вместимости совокупности довольно дорого потому что увеличивающий код создаёт новую совокупность с большей вместимостью, копирует все элементы текущей совокупности, затем большая совокупность становится новой. Если ты знаешь насколько большой должна быть совокупность, или представляешь начальную вместимость, более эффективно создать совокупность при помощи сообщения \verb|new:| где параметр это начальная вместимость. Некоторые совокупности, такие как Массив, не увеличиваются автоматически, поэтому важно определять их вместимость (для совокупностей которые на увеличиваются автоматически, размер и вместимость это одно и то же). Например,


массив := Массив новый: 10.
совокупность := УпорядоченнаяСовокупность новый: 20.
поток := (Поток новый: 100) writeStream.

Методы \verb|новый| и \verb|новый:| реализованы в \verb|Behavior|, однако они переопределены во многих классах. Из за того что твой класс наследует \verb|новый| и \verb|новый:|, ты не должен переопределять их пока твой класс ---. Есть два типа присвоения которые могут быть сделаны: присваивание переменной начального значения по умолчанию, и присваивание переменной значения специфичного для экзэмпляра. Давай рассмотрим каждый вариант.


Присваивание значений по умолчанию

править

Допустим у нас есть объект \emph{Сотрудник} который следит за дозволенным и реальным количеством часов простоя и времени болезни. Когда экземпляр \verb|Сотрудника| создаётся, мы хотим инициализировать его переменные экземпляра подходящими значениями, не обращая внимания на имя или жалование сотрудника. Есть два способа сделать это. Ты можешь использовать lazy инициализацию как мы делали в Главе 4, Переменные. Например,


Сотрудник>>sickHoursUsed
   ^sickHoursUsed неОпределено?
      истина: [sickHoursUsed := 0] 
      ложь:    [sickHoursUsed]

В качестве альтернативы, ты можешь инициализировать все данные в одном методи \verb|инициализация|. Lazy инициализация полезна ести твой объект никогда не использует данные которые сложны для инициализации, но на доступ к данным тратится дополнительное сообщение при каждом обращении. Если ты хочешь ---. Например, для объекта \verb|Сотрудник| мы может быть следующий метод инициализации.


Сотрудник>>инициализация
   сам времяБолезни: 0.
   сам время---: 0.
   сам допустимоеВремяБолезни: 80.
   сам допустимоеВремя---: 80.

Для вызова метода \verb|инициализация| ты должен переопределить метод \verb|новый| т.к. наследуемый \verb|новый| не вызывает \verb|инициализацию| (\potom). Обычный способ переопределения \verb|новова| показан в следующем примере (\potom).


класс Сотрудник>>новый
  ^супер новый инициализация

До того как ты переопределиш \verb|новый| как показано здесь, ты должен знать что \verb|супер новый| существует. Если метод \verb|новый| в суперкласс посылает сообщение \verb|инициализация|, твой метод \verb|инициализация| будет вызван дважды, один раз при вызове метода \verb|новый|, затем второй раз при вызове метода \verb|новый| \emph{Сотрудника}. В этой ситуации ты не должен переопределять \verb|новый| пока ты можешь наследовать его из суперкласса. Т.к. в суперклассе есть метод \verb|инициализация| предположительно инициализирует данные суперкласса, твой метод \verb|инициализация| должен начинаться со строки \verb|супер инициализация|. Например, допустим что есть класс \emph{Сотрудник}, с подклассами \emph{ВременныйСотрудник} и \emph{ШтатныйСотрудник}. Давай примем что временный сотрудник работает две недели а штатный сотрудник работает три недели. Мы можем получить следующее:


класс Сотрудник>>новый
  ^супер новый инициализация
Сотрудник>>инициализация
  сам продолжытельностьБолезни: 0.
  сам продолжытельностьРаботы: 0.
ВременныйСотрудник>>инициализация
  супер инициализация
  сам допустимоеВремяБолезни: 80.
  сам допустимоеВремяРаботы: 80.
ШтатныйСотрудник>>инициализация
  супер инициализация
  сам допустимоеВремяБолезни: 120.
  сам допустимоеВремяРаботы: 120.

Обычный способ сделать переопределения метода \verb|новый| --- \verb|^супер| \verb|новый| \verb|инициализация|, некоторые люди предпочитают сообщение \verb|простойНовый|.


класс МойКласс>>новый
  ^сам простойНовый инициализация

Методы которые начинаются с \verb|простой|, таких как \verb|проснойНовый| и \verb|простойОт:|, не могут быт переопределены. Их предназначение предоставлять основную функциональность, и программисты дожны быть способны полагаться на это. Если ты хочешь переопределить функциональность, переопредели \verb|новый| и \verb|от:|. При использовании \verb|простойНовый|, ты не должен беспокоиться о том что любой суперкласс пошлёт сообщение \verb|инициализация| и из за этого твой метод \verb|инициализация| выполнится более чем один раз. Однако, ты по прежнему определяеш когда ты должен посылать сообщение \verb|супер инициализация| в своём методе \verb|инициализация|.


Переопределение метода новый

править

\potom. Одно из решений --- наследовать все классы твоей программы от \verb|МояПрограммаОбъект|, который заменяет \verb|Объект|. В объекте МояПрограммаОбъект, ты переопределяеш метод \verb|новый| на стороне класса, и пишыш метод \verb|инициализация| на стороне экземпляра. Сейчас ты можешь переопределить \verb|инициализацию| в твоём классе без переопределения метода \verb|новый|.


класс МояПрограммаОбъект>>новый
  ^супер просойНовый инициализация
МояПрограммаОбъект>>инициализация
"ничего не делать"

Присвоение экземплярам особых значений

править

Часто, когда ты создаёшь новый экземпляр, ты нуждаешься в присвоении ему некоторой информации. В нашем примере сотрудника, мы нуждаемся менее всего в имени. Мы также возможно нуждаемся в задании номера социальной страховки, зарплаты и информации о поле, материальном положении и количестве ---. Есть два пути: создать экземпляр а затем присвоить значения переменым, или задать переменные во время создания экземпляра. For the sake of example, давай примем что когда создаётся объект сотрудник, две части информации абсолютно необходимы: имя и номер социального страхования. Если мы создадим экземпляр затем присвоим переменные, мы возможно поступим как-нибудь так:


сотрудник := Сотрудник новый.
сотрудник := имя: аИмя.
сотрудник номерСоциальногоСтрахования: аНомер.

Проблема этого подхода в том, что ты полагаешся на то, что все программисты не будут забывать присвоить требуемые переменные после создания экземпляра. Это нормально, если переменная необязательная, но опасно, если она обязательная. Если ты хочешь гарантировать, что данные присвоены, лучше (better off) написать метод создания экземпляра который заставляет программиста предоставить нужную информацию. Например, если мы напишем наш собственный метод создания, мы можем создать сотрудникак как здесь:


сотрудник := Сотрудник имя: аИмя номерСоциальнойСтраховки: аНомер

Как должен выглядеть метод \verb|имя:номерСоциальноСтраховки:|? Один из вариантов - просто передть методу инициализации информацию, которую нужно присвоить.


класс Сотрудник>>имя: аИмя номерСоциальнойСтраховки: аНомер
  ^супер новый инициализироватьИмя: аИмя 
               номерСоциальноСтраховки: аНомер

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


класс Сотрудник>>имя: аИмя номерСоциальногоСтрахования: аНомер
  | экземпляр |
  экземпляр := супер новый.
  экземпляр имя: аИмя.
  экземпляр номерСоциальногоСтрахования: аНомер.
  ^экземпляр
класс Сотрудник>>имя: аИмя номерСоциальногоСтрахования: аНомер
  ^супер новый
      имя: аИмя;
      номерСоциальногоСтрахования: аНомер;
      тысам

Переопределение метода новый для предотвращения его использования

править

Если ты требуешь от программистов использовать \verb|имя:номерСоциальногоСтрахования:| для создания экземпляра \emph{Сотрудник}, ты можешь переопределить \verb|новый| так чтобы он вызывал исключение. Этот способ не очень общий, но он позволяет программисту легко определить, что он неправильно создаёт экземпляр объекта сотрудник.


класс Сотрудник>>новый
  сам ошыбка:'Пожалуйста используй 
              имя:номерСоциальногоСтрахования:
              для создания экземпляров Сотрудник'

Опасайтесь использовать метод новый:

править

Если только нужно имя сотрудника, ты можешь временно использовать \verb|новый: аИмя|. Воздержывайтесь от временных решений. Метод создания экземпляра \verb|новый:| используется для задания размера совокупности, и программисты читающие код должны able to assume что создаётся совокупность когда они видят \verb|новый:|. Вместо этого используй \verb|имя:| или \verb|новыйИменуемый:| или \verb|новыйСИменем:|. Я склоняюсь использовать имена методов, которые говорят мне обе вещи: что они создают новый экземпляр и что параметр означает.


Классы с единственным экземпляром

править

Некоторые классы имеют только один экземпляр. Примерами из системных классов являются \verb|истина|, которая является единственным экземпляром класса \verb|Истина|, \verb|ложь|, экземпляр \verb|Ложь|, \verb|пусто|, экземпляр \verb|НеопределённыйОбъект|, и \verb|Процэссор|, экземпляр \verb|ПланировщикПроцэссора|. Классы \verb|НеопределённыйОбъект|, \verb|Логиче--| и \verb|ПланировщикПроцэссора| переопределяют метод \verb|новый| для предотвращения создания экземпляров.


В твоём собственном коде, если у тебя есть класс который должен иметь только один экземпляр, самым простым способом добиться этого завести переменную класса которая содержыт единственный экземпляр. Когда кто-нибудь попытается создать новый экземпляр после того как создан первый, ты можешь или вызвать ошибку или возвратить первый экземпляр. Например,


класс МойКласс>>новый
  экземпляр пусто? ложь? [сам ошибка: 'Ты можешь использовать
                         только один экземпляр МоегоКласса'].
  экземпляр := сам простойНовый.
  ^экземпляр
класс МойКласс>>новый
  ^экземпляр пусто?
      истина? [экземпляр := сам простойНовый]
      ложь? [экземпляр]

Специальные переменные, буквы и символы >