Smalltalk в примерах/Переменные: различия между версиями

Материал из Викиучебника — открытых книг для открытого мира
Содержимое удалено Содержимое добавлено
переименовал «Smalltalk в примерах:Переменные» в «Smalltalk в примерах/Переменные»: Унификация разделителей в книгах (см. [[Викиучебник:Фору
м Категоризация по запросу на w:ВП:РДБ
Строка 706: Строка 706:


[[Smalltalk в примерах/Создание экземпляра|Создание экземпляра →]]
[[Smalltalk в примерах/Создание экземпляра|Создание экземпляра →]]
[[Категория:Smalltalk в примерах|Переменные]]

Версия от 09:26, 27 сентября 2009

В этой главе мы рассмотрим различные типы переменных которые есть в Смолтоке: Экзэмпляр, класс, экзепмляр класса, параметр, временная переменная. Глобальные переменные большая тема сама по себе поэтому мы рассмотрим её в Главе 7, Глобальные переменные. В Смолтоке также есть специальные переменные которые мы рассмотрим в Главе 6, Специальные переменные, буквы и символы.


Имена переменных

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


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


"экземпляры или временные переменные"
имяСлужащего
совокупностьСтрок
верхнийЛевыйУголПрямоугольника
"параметры"
число
совокупностьСлужащих
"класс, экзэмпляр класса или глобальная переменная"
Служащий
ДеньМесяца
НазваниеМесяца

Я всегда нахожу удобным использовать приставку в имени класса указывающую на имя программы или её компонента потому что мне нравится знать откуда класс когда я просматриваю код. Например, если у вас бухгалтерская программа с платёжными ведомостями, кредиторскими задолженностями и бухгалтерскими книгами, вы можете использовать такие приставки для компонентов: Pr, Ap, Gl, и что-нибудь подобное Aac для классов используемых для взаимодействия между компонентами (Accounting Appliction Communication). Классы общего назначения которые будут использоваться различными программами или компонентами имеют general company приставку, или приставку Sup.


Переменные экземпляра

Допустим, у нас есть класс Друг. Каждый друг в нашей программе имеет имя, фамилию и номер телефона. Определение класса Друг указывает что каждый экземпляр Друга имеет три переменных: имя, фамилия, телефон. Мы указываем эти переменных экземпляра в определении класса.


Объект подкласс: #Друг
  именаПеременныхЭкземпляра: 'имя фамилия телефон'
  именаПеременныхКласса: 
  poolDictionaries: 
  категория: 'МояКатегория'

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


первыйДруг := Друг
  новаяФамилия: 'Иванов'
  имя: 'Ваня'
  телефон: '555-5555'

Если мы затем создадим другого друга, второйДруг, он не будет влиять на первогоДруга, потому что первыйДург имеет свои собственные переменные экземпляра.


второйДруг := Друг
  новаяФамилия: 'Петров'
  имя: 'Петя'
  телефон: '111-1212'

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


Переменные класса

Предположим у нас есть несколько экземпляров класса Дата. Каждый экземпляр должен быть уникальным, и должен иметь свои собственные значения для переменных экземпляра определённых Датой ( это год, месяц и день, количество дней от начала года). Мы должны знать имена месяцев для каждого экземплряра. Кто-то должен отслеживать имена месяцев, и хотя имена месяцев ассоциируются с датами, мы можем отслеживать имена где нибудь в классе Дата. Однако, мы не должны растрачивать память на то чтобы иметь для каждого экземпляра Даты этот массив. Вместо этого, мы помещаем массив имён месяцев в переменную класса. Это позволяет классу Дата отслеживать массив, таким образом мы имеем одну копию массива. Все экземпляры имеют доступ к переменным класса их класса, т.е. каждый экземпляр Даты может обращаться к массиву имён месяцев. Фактически, у Даты есть несколько переменных класса, как показано в следующем определении класса.


Величина подкласс: #Дата
  именаПеременныхЭкзэмпляра: 'день год'
  именаПеременныхКласса: 'ДнейВМесяце ПервыйДеньМесяца
               ИменаМесяцев СекундВДне ИменаДнейНедели'
  poolDictionaries: 
  категория: 'Magnitude-General'

Есть два примера когда экземпляр Даты ссылаются на переменные стороны класса. Сообщения имяМесяца деньВМесяце используют массивы содержащиеся в переменных класса ИменаМесяцев и ДнейВМесяце.


(Дата новыйДень: 115 год: 1960) имяМесяца. #Апрель
Дата сегодня имяМесяца.                    #Октябрь
Дата сегодня днейВМесяцэ.                        31

Все экзэмпляры могут напрямую ссылаться на их переменные класса, хотя возможно предпочтительней обращаться к ним через accessors, которые мы рассмотрим позже в этой главе. Хотя экземпляр имеет доступ к переменным класса, сам класс не имеет доступа к переменным экземпляра его экземплряров, и фактически, класс обычно не ---. Вы можете просматривать переменные класса --- просматривая класс.


Классы, ссылающиеся на свои экземпляры ()

---

Переменные ---

Параметры

Параметры (или аргументы) это объекты которые передаются методу или блоку. В Смолтоке нет проверки на типы, поэтому вы можете передавать любые объекты как параметры любому методу. Конечно, когда вы посылаете сообщение объекту-параметру, будут проблемы если объект не понимает сообщения - если объект не подходящего типа. (Некоторые люди в шутку говорят что Смолток очень строго типизирован - все аргументы должны быть объектами!) В отличии от других типов переменных, вы не можете присвоить другое значение переменной параметру. К счастью, компилятор обнаржит это, поэтому вы не сможете выполнить следующий пример.


МойКласс>>делайЧтоЛибо: объект
  объект ← 3.

Однако, вы можете изменить содержание параметра, т.е. совершенно законно выполнить следующий код, хотя --- хороший стиль.


МойКласс>>изменитьСовокупность: совокупность
  совокупность добавить: 3.

Параметры методов

В коде показанном ниже, мы видим различные примеры сообщений и параметров. Ниже показано несколько примеров имён методов и параметров с различными описаниями.


Дата класс>>новыйДень: номерДня год: соотвГод
Объект>>---
УпорядоченнаяСовокупность>>добавить: новыОбъект
Словарь>>от: ключь поместить: объект
МойКласс>>изменитьСовокупность: совокупность

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


В примере приведённом выше, параметром для новыйДень: должно быть целое число, но это даёт мало информации о том что должно содержать это число. Поэтому, параметра назван номерДня. Так же, мы можем иметь метод который создаёт новый экземпляр Сотрудника.


Сотрудник класс>>имя: имя фамилия: фамилия

Будет более понятным, если дать параметрам имена имя и фамилия а не строкаОдин и строкаДва. Другое имя строкаИмя не добавляет информации, особенно пока метод возможно ничего не делает с параметром, кроме как помещает значение в переменную экземпляра.


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


Параметры блоков

Блок содержит код который не выполняется до тех пор пока блок не получит сообщение из family значение. Чтобы передать параметры в блок, сообщение должно иметь параметры, такие как значение: или значение:значение:. Например, мы можем иметь что то вроде этого.


блок := [ :имяСтроки | Transcript cr;
                      показать: 'Имя: ', строкаСИменем].
блок значение: 'Саня'.
блок := [ :строкаСИменем : возраст: | 
        Transcript cr;
          показать: 'Возраст ', строкаСИменем, возраст, 'лет'].
блок значение: 'Dave' значение: 12.

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


[ :параметр1 :параметр2 :параметр3 :параметр4 :параметр5 |
  сам делайЧтоНибудь]
значениеСАргументами: \#(99 88 77 66 55).

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


совокупность делать: 
   [ :каждый | 
     Transcript cr; 
     показать: каждый строкаДляПечати
   ].
совокупностьЧисел собрать: 
  [ :каждый | каждый*30].
совокупностьЧисел ввести: 0 в: 
  [ :подуровень :каждый | подуровень + каждый ].

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


1 до: 5 делать:
  [ :индэкс | я делатьЧтоНибдьИспользуч: индэкс ].
'сейчас время' делать:
  [ :буква | я делатьЧтонибудьИспользуя: буква].

Обычно, ---


#('cat' 'dog' 'gerbil')
collect: [ :pet | #('milk' 'water' 'oj')
collect: [ :drink | pet -> drink]]

Временные переменные

Временные переменные - это переменные, которые существуют короткое время: пока выполняется метод или блок кода. Есть несколько причин для использования временных переменных. Наиболее важная причина сохранить значение, которое не может получено повторно. Например, если вы читаете объект из потока(stream) или разделяемой очереди (shared queue), и вы хотите использовать этот объект несколько раз, используйте временную переменную для сохранения объекта. Например:


запрос := sharedQueue слудующий.
автор := запрос автор.
времяЗапроса := запрос времяСоздания.

Другой причиной использования временной переменной является предотвращение сложных вычислений. Например, если вы сравниваете переменную со значением из базы данных, естественно сохранить значение из базы данных во временной переменной. Сравните два следующих примера.


совокупность найти: [ :каждый | каждый = я моёЗначениеИзБД ]
еслиНет: [ноль].
значениеИзБД := я моёЗначениеИзБД.
совокупность найти: [ :каждый | каждый = значениеИзБД]
еслиНет: [ноль].

Третья причина использования временной переменной улучшэние производительности из за уменьшэния количества посылаемых сообщений. Например, в первом примере вышэ, мы несколько раз посылали несколько одинаковых сообщений. Можно сохранить несколько сообщений если мы сделаем так как показано в следующем примере.


сам проверитьАдрес: 
 (совокупностьСотрудников от: идентификаторСотрудника) адрес.
сам утердитьЗарплату:
  (совокупностьСотрудников от: идентификаторСотрудника) зарплата.
сам напечататьЧек:
  (совокупностьСотрудников от: идентификаторСотрудника).
сотрудник :=
  (совокупностьСотрудников от: идентификаторСотрудника).
сам утердитьЗарплату: сотрудник зарплата.
сам напечататьЧек: сотрудник.

Четвёртая причина использовать временные переменные - сделать код легче для понимания. Иногда трудно понять какой объект мы получим в результате сложной последовательности сообщений. Даже если мы не нуждаемся в использовали временной переменной при выполнении последовательность сообщений один раз, можно сделать код более читабельным поместив результат этой последовательности в хорошо названную переменную. (Альтернативный подход заключается в вычислении результата другим методом и замене сложных сообщений на одно сообщение) Например, следующий код позволяет проще понять что происходит без необходимости чтения кода блока сортировки.


МойКласс>>сортироватьПоДнюРождения: совокупность
  | блокСортировкиПоДнюРождения |
  блокСортировкиПоДнюРождения := 
    [ :первый :второй | 
      первый деньРождения <= второй деньРождения ].
  ^совокупность какСортированнаяСовокупность: 
    блокСортировкиПоДнюРождения.

Временные переменные в методе

Временные переменные называются между вертикальными чертами до любого кода в методе. Например,


копироватьС: новыйЭлемент | новаяСовокуность | новаяСовокупность := я копировать. новаяСовокупность добавить: новыйЭлемент. \^{}новаяСовокупность


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


Временные переменные в блоках

Временные переменные в блоках помещаются между вертикальными чертами, точно так же как временные переменные в методе. Например.


[ | результат | результат := я делайЧтоНибудь. ... ]

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


мойМетод совокупность делать: [ :каждый | | результат |

          ...

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


Глобальные переменные

Глава 7, Глобальные переменные, описывает глобальные переменные, поэтому мы не будем обсуждать их здесь.

Переменные как слот

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


Из за того что объект существует отдельно и только помещается в переменную, нет причины по которой объект не можэт быть помещён в различные переменные одноременно. Мы можэм просто получить следующую ситуацыю. Когда вы просмотрите г, вы увидите что она содержыт три элемента, числа 1, 2 и 3. Множэственное присвоение, как показано в примере, легально но является прохим стилем.


 а := б := в := г := УпорядоченнаяСовокупность новый.
 а добавить: 1.
 а добавить: 2.
 а добавить: 3.
 г просмотреть.

В данном примере, мы присваиваем один и тот жэ экзэмпляр УпорядоченнойСовокупности всем переменным. Когда мы изменяем совокупность через одну переменную, её содержымое изменяется и эти изменения видны всем переменным которые содержат эту совокупность. Это показано на Рисунке~\ref{odinOb7ekt}.


\begin{figure}[!htb]

 \begin{center}
 \includegraphics{odinOb7ekt.eps}
 \end{center}
 \caption{Все переменные содержат один объект}
 \label{odinOb7ekt}

\end{figure}

С другой стороны, если мы сделаем следующее, мы увидим что б содержыт число 2, в то время как г содержыт числа 1 и 3.


 а := б := в := г := УпорядоченнаяСовокупность новый.
 а добавить: 1.
 б := 2.
 в добавить: 3.
 б просмотреть.
 г просмотреть.

В этом примере, мы присвоили различные значения б, и связали с ней объект 2 вместо экзэмпляра УпорядоеннойСовокупности. Это показано на Рисунке~\ref{raznyeOb7ekty}.


\begin{figure}[!htb]

 \begin{center}
 \includegraphics{raznyeOb7ekty.eps}
 \end{center}
 \caption{Переменные содержат различные объекты}
 \label{raznyeOb7ekty}

\end{figure}

Методы доступа

Методы доступа это методы которые позволяют вым получать и присваивать значения переменным экзэмпляра и класса. Т.к. переменные экзэмпляра более сложно использовать чем переменные класса, вы можете более часто увидеть использование методов доступа для переменных экзэмплра. Из за того что они получают и присваивают переменные, методы доступа ещё известны как getters and setters, и когда вы пишите метод доступа, вы должны писать и getter and setter. По соглашэнию они именуются так жэ как и переменная экзэмпляра. Например,


Сотрудник>>зарплата: число
зарплата := число
Сотрудник>>зарплата
^ зарплата

Если вы используете позднюю иницыализацыю (иницыализацыя переменной когда она нужна в первый раз) вместо иницыализацыи переменных экзэмпляра методом иницыализировать, метод доступа зарплат дожэн выглядеть примерно так:


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

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


Методы доступа для совокупностей

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


Сотрудник>>навыки
  ^совокупностьНавыков копировать
Сотрудник>>добавитьНавык: навык
  ^совокупностьНавыков добавить: навык
Сотрудник>>удалитьНавык: навык
  ^совокупностьНавыков удалить: навык еслиНет: [ноль]

Однако, вы ---. После всего, мы можете решить изменить совокупность УпорядоченнаяСовокупность на Массив или Словарь и вы не должны концентрироваться на том как другие программы обращаются к совокупности. Предоставив несколько методов доступа таких как добавитьНавык: и удалитьНавык:, вы не должны открывать текущую совокупность для всех. (Друое применение ---)


Пять применений методов доступа

У меня есть друг который называет "варварским" применение методов доступа для переменных экзэмпляра. Для каждой переменной экзэмпляра, существует пять методов доступа. Используя переменную зарплата например, мы должны иметь следующие пять методов доступа. Только два из них открытые зарплата и зарплата:. Остальные закрытые. Открытые методы доступа предназначены для получения побочного эффекта (например, установление связи), в то время как закрытые методы не предназначены для получения побочного эффекта. Однако не многие люди строго используют эту технику.


Работник>>зарплата
"Возвращает зарплату. Этот метод доступа предназначен
 для получения побочного эффекта."
  сам мояУзнатьЗарплату == ноль
   еслиИстина: [я мояНазначитьЗарплату: я мояВычислитьЗарплату]
  ^сам мояУзнатьЗарплату
Работник>>мояУзнатьЗарплату
"Возвращает зарплату. Нет побочных эффектов."
  ^зарплата
Работник>>мояВычислитьЗарплату
"Возвращает начальное значение зарплаты, возможно 
 вычисляет её."
  ^сам базаваяЗарплатаДляРазряда * я районныйКоэффицыэнт
Работник>>зарплата: значение
"Устанавливает зарплату. Этот метод доступа предназначен
 для получения побочного эффекта."
  сам мояЗадатьЗарплату: значение.
  сам уведомитьДиспечераПлатёжнойВедомости
Работник>>мояЗадатьЗарплату: значение
"Устанавливает зарплату. Нет побочных эффектов."
  зарплата := значение

Метод доступа или прямая ссылка

Есть школы как экзэмпляры должны ссылаться на свои переменные экзэмпляра. Одна школа говорит что переменные экзэмпляра должны всегда ссылаться не напрямую, через методы доступа. Другая школа говорит что иногда на переменную экзэмпляра можно ссылаться напрямую. Давайте рассмотрим эти две идеи.


Прямая ссылка

Есть три приемущества прямой ссылки на переменную экзэмпляра. Первое, вы не должны писать метод доступа (однако VisualWorks CodingAssistant помогает вам делать это). Второе, немного более эффективно обращаться к переменной напрямую потому что вы сохранете посылку сообщения при каждом доступе (однако незначительно, т.к. компилятор можэт оптимизировать эту посылку сообщения). Однако, наиболее значительное приемущество обращения к переменной напрямую


Ссылка через метод доступа

\potom


Account>>balance: aFloat
balance := aFloat
Account>>balance
^balance


Account>>balance: aFloat
balance := (aFloat * 100) rounded
Account» balance
Abalance / 100


MyClass»weight: aNumber
oldWeight := weight,
weight := aNumber.
self markDirty.
self changed: #weight with: oldWeight

Какой способ выбрать

\potom


Последовательные методы доступа

\potom


companyName := employee department division company companyName.


companyName := employee companyName.


Employee>>companyName
Aself company companyName
Employee>>company
Aself department company


\begin{figure}[!htb]

 \begin{center}
 \includegraphics{loose.eps}
 \end{center}
 \caption{\potom}
 \label{loose}

\end{figure}

Документирование при помощи имён

Documentation on variables

Все переменные экземпляров, классов, и экземпляров классов должны быть задокоментированны в комментарии класса. Следует описывать по крайней мере тип, назначение переменной, а также дополнительную специальную информацию, которая будет полезна другим программистам. Например, это часть комментария для класса Дата.


Instance Variables:
day <Integer> from 1 to (365 | 366)
year <Integer> typically after the year 1900
Class Variables:
DaysInMonth <Array of: Integer> the number of days in each
month
MonthNames <Array of: Symbol> the names of the 12 months

Когда вы определитесь, какая информация должна содержаться в комментарии класса, вы можете изменить комментарий класса, изменив метод \verb|образецСтрокиКомментария| в \verb|ОписаниеКласса|.


Создание экземпляра →