Уроки программирования на Smalltalk: создание объекта aPerson
Статья #4

  Автор: Jason Steffler
Перевод: © Роман Шумихин.


 

Краткое содержание этой статьи

    Если вы не читали предыдущие статьи, сначала обязательно прочтите введение.  В этом месяце мы будем обсуждать создание объекта aPerson : как для этого использовать браузеры и некоторые принципы объектно-ориентированного программирования.  Если вы хотите получить все статьи или получить информацию о будущих статьях, вы можете посетить страницу MST.  Дальнейшую информацию по изучению Squeak можно найти здесь.
    В этом месяце я не буду использовать локальные связи (local links [LL]), так как они не использовались так часто, как я ожидал.

Цитата дня

"Самая большая проблема C++ сообщества сегодня - это использование Стандартного C++ так, как он был задуман, а не как прославленный C или жалкий Smalltalk."
   - Бьярн Страуструп (создатель языка C++), "Getting From The Past To The Future" , стр. 23 C++ Report, SIGS Ноябрь/Декабрь 1999 Том1 No.10

Создание объекта aPerson

    Прежде, чем мы начнем, вы, вероятно, захотите скачать файл со следующей версией ScopedBrowser , который мы рассмотрели в предыдущей статье.  Если вы сохранили свой образ с браузером, не беспокойтесь о конфликтах, так как этот новый файл заменит 1 предыдущий ScopedBrowser ( см. статью 2 о том, как это делается) На этот раз мы рассмотрим еще несколько классов, они использовались для создания класса Person. И несколько классов для лучшего восприятия материала:
  • Boolean (суперкласс (super class) Истины (True) и Лжи (False))
  • False (Ложь)
  • Object (объект) - базовый объект языка Smalltalk, все остальные объекты языка являются его подклассами2
  • True (Истина)
  • Workspace (рабочее пространство) - класс, который мы будем использовать для открытия текстовых окон
  • ScopedBrowser - на тот случай, если вам интересно, что за браузер мы используем.  Если вы хотите поэкспериментировать с ним, рекомендую предварительно сохранить ваш образ, так как очень легко испортить что-нибудь в браузере, который вы используете для экспериментов.
    Что касается заметки выше, в Smalltalk вы можете экспериментировать с чем угодно, включая и базовые классы (base classes).  Хотя вы можете (а иногда и должны) внести изменения в базовые классы (base classes), я не рекомендую вам этого делать, так как можно легко испортить образ или нарушить совместимость с будущими версиями.  Если позволит время/размер, в следующей статье мы уделим больше внимания изменению базовых классов.   А пока обязательно сохраняйте ваш образ прежде чем вносить изменения в базовые классы. Если вы решили сохранить измененный базовый класс, всегда имейте под рукой архивную копию вашего образа до изменений.
    Теперь вернемся к созданию класса Person. Если вы ввели исходный код класса Person из статьи 2, то, возможно, вы захотите или удалить тот класс из системы или озаглавить этот пример как Person2, или JrsPerson (префикс с вашими инициалами), или что-нибудь типа того, чтобы избежать коллизий пространства имен (namespace collisions).
    Я удалю класс Person и создам новый, если вы решите последовать моему примеру, откройте ScopedBrowser, используя такую команду:

    ScopedBrowser openBrowserForArticle4

    Вы увидите семь доступных вам категорий, включая MakingSmalltalk-Article2.  Если класс Person существует, то он должен быть в этой категории.  Щелкните левой кнопкой мыши на классе Person, потом нажмите среднюю кнопку мыши и выберите пункт Remove.  Появится окошко с просьбой подтвердить ваш выбор. Щелкните на кнопке yes.  Вы увидите, что категория MakingSmalltalk-Article2 теперь пуста.
    Чтобы теперь объявить наш класс Person, щелкните левой кнопкой мыши в окне ScopedBrowser на категории MakingSmalltalk-Article4, вы увидите, что в панели с исходным кодом появился хороший шаблон класса для вас.  Здесь все понятно:

Object subclass: #ИмяКласса
instanceVariableNames: 'instVarName1 instVarName2'
classVariableNames: 'ClassVarName1 ClassVarName2'
poolDictionaries: ''
category: 'MakingSmalltalk-Article4'
    Пока не задумывайтесь о том, что такое общий словарь (pool dictionary) или переменная класса (class variable). Просто отредактируйте этот шаблон так, чтобы он выглядел как здесь:
[ex1a]
Object subclass: #Person
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'MakingSmalltalk-Article4'
    Теперь щелкните средней кнопкой мыши и выберите пункт меню accept.  Вы заметите, что класс Person добавился к категории article 4.  Теперь давайте добавим метод. Первый метод, который мы обсуждали в статье 2, был sing. Щелкните правой кнопкой мыши и в панели категорий методов выберите пункт new category...>new..., введите: Singing в качестве имени категории методов, потом щелкните на кнопке Accept.  На кодовой панели вы заметите хороший шаблон для метода:
message selector and argument names
 "comment stating purpose of message"

 | temporary variable names |
 statements

    Скопируйте шаблон с нашим методом sing поверх этого метода, потом щелкните правой кнопкой мышки на accept.
[ex1b]
sing
    (Workspace new contents: 'Do be do be doooooo.') openLabel: 'A bad impression of Sinatra'.
    Метод sing появился в категории методов Singing.  Теперь перейдите в рабочее пространство и наберите: Person new sing. Появится bad impression.  Вот как это выглядит:

    Следующий метод, который мы обсуждали был sing:, он имел один параметр: 'MaryHadALittleLamb'. Теперь вы видите очевидную трудность нашего первого метода: song и label запрограммированы только для того, что мы туда поместим.  Более разумным будет создать метод, который знает, что мы поем, и который сможет получать параметры от различных методов чтобы упростить или использовать повторно наш код.  Таким образом, если вдруг вы захотите скорректировать процесс воспроизведения пения, придется обновлять гораздо меньше мест.  Давайте создадим новый метод (помните, что мы называем наши собственные методы, используя префикс 'my'), который определяет, как мы будем воспроизводить пение на основе того, что поется:
[ex1c]

mySing: someLyrics withTitle: aTitle
    (Workspace new contents: someLyrics) openLabel: aTitle
    Давайте оптимизируем (refactor) наш первый метод sing чтобы использовать этот закрытый (private) метод. Просто скопируйте его прямо на метод sing или отредактируйте текст, приняв его (accept):
[ex1d]
sing
    self mySing: 'Do be do be doooooo.' withTitle: 'A bad impression of Sinatra'
    Обратите внимание, текст и название песни все еще явно определены в публичном (public) методе sing, поэтому они недоступны прямо из других объектов и методов Person, но это уже лучше, чем было раньше.   Мы рассмотрим лучший способ организации вашего кода, когда будем заниматься следующей песней, и проиллюстрируем, как может быть полезно вынесение текста песни за рамки метода.   Имея это в виду, давайте создадим метод lyrics для нашей следующей песни:
[ex1e]
maryHadALittleLambLyrics
    ^'Mary had a little lamb, little lamb, little lamb, Mary had a little lamb whose fleece was white as snow.'
    Теперь мы можем добавить наш метод sing:, который использует наш закрытый метод mySing:withTitle:.  Он достаточно прост и, по умолчанию, имеет значение 'A bad impression of Sinatra', если не понятно, что метод запросили спеть.   Важно:  когда мы просим спеть aSong, если он равен 'MaryHadALittleLamb', то возвращается булев объект (boolean object). Потом мы определяем значение этого булева объекта, правда он, или ложь.
[ex1f]
sing: aSong
    aSong = 'MaryHadALittleLamb'
        ifTrue: [self mySing: self maryHadALittleLambLyrics withTitle: 'Mary had a little lamb']
        ifFalse: [self sing].
     Помните, что вы можете сразу запустить только что добавленный метод, так как не нужно никаких циклов компиляции-связывания-запуска.  Наконец, давайте добавим наш последний поющий метод, который похож на метод выше:
[ex1g]
sing: aSong andDoIt: anAdjective
    aSong = 'MaryHadALittleLamb'
        ifTrue: [self mySing: self maryHadALittleLambLyrics inManner: anAdjective withTitle: 'Mary had a little lamb']
        ifFalse: [self sing].
     Этот метод использует другой закрытый метод, который строится на нашем первом закрытом методе.   Теперь вы видите, как вынесение текстов песен в отдельный метод может быть полезным: вы можете получить к ним доступ когда нужно. Чтобы петь громче используется верхний регистр, тише - нижний регистр.
[ex1h]
mySing: someLyrics inManner: anAdjective withTitle: aTitle
 "Using simple logic here for illustrative purposes - if the adjective is not 'loudly' or 'quietly' just ignore how we're being asked to sing"
 | tmpLyrics |
    anAdjective = 'loudly'
        ifTrue: [tmpLyrics := someLyrics asUppercase].
    anAdjective = 'quietly'
        ifTrue: [tmpLyrics := someLyrics asLowercase].
    self mySing: tmpLyrics withTitle: aTitle
     Я опускаю описание метода whatIsMyHeight, так как он очень похож на то, что мы только что рассмотрели.  Обратите внимание на простую условную логику, которую мы использовали здесь.   Ее можно избежать программными методами, такими, как двойная доставка (double dispatching), но это выходит за рамки данной статьи.   Возможность обойтись без условной логики (conditional logic) обычно считается одним из преимуществ объектно-ориентированного программирования.  Чтобы это проиллюстрировать, представьте себе, что у нас есть 30 различных песен, которые нужно спеть. Это заставит нас иметь 30 различных конструкций ifTrue или использовать оператор CASE (большинство версий Smalltalk даже не имеет такого оператора CASE) с 30-ю различными случаями.  И если мы захотим добавить еще 5 песен - придется отследить все места, где мы описали предыдущие 30 песен и добавить туда наши дополнительные 5; скорее всего таких мест в программе больше, чем одно, поэтому у вас, вероятно, возникнут проблемы с синхронизацией.   Но, для простоты изложения, мы все же используем здесь условную логику.
 

Забегая вперед

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

Как пользоваться отладчиком в Squeak

    В этой секции я не буду объяснять код программы или примеры, мы попробуем разобраться в кое-чем другом.   В этом месяце мы разберемся с отладкой (debugging), так как это то, что я считаю хорошей чертой Smalltalk.    Вы можете отлаживать и исправлять ваши программы в реальном времени прямо в отладчике, при этом не нужно никаких циклов компиляции-связывания-запуска.
    Чтобы проиллюстрировать это, где-нибудь в код aPerson мы вставим сообщение (message) halt.  Отредактируйте метод sing: в качестве первой строки поместите self halt.  Ваш метод должен выглядеть так, как здесь:
[ex2]

    self halt.
    self mySing: 'Do be do be doooooo.' withTitle: 'A bad impression of Sinatra'.

    Теперь попросите вашего человека запеть с помощью такой команды: Person new sing. Вы увидите отладчик:

  Щелкните на кнопке Debug и щелкните на 2-ом методе в стеке методов. Вот как это выглядит:

   Теперь удалите self halt., затем щелкните средней кнопкой мыши и выберите в меню пункт accept - в этом месте ваш код обновлен так, что любые запросы к методу sing получают новую версию. Вы можете продолжить выполнение: поместите курсор мышки над верхней панелью, щелкните средней кнопкой мыши и выберите пункт меню proceed
    Это очень удобный метод программирования/отладки вашего кода.  Помните, отладчик - ваш друг. Много начинающих программистов на Smalltalk не используют отладчик по полной программе, потому что у них укоренился цикл компиляции-связки-запуска.   С помощью этой техники, вы используете отладчик для пошагового выполнения программы до тех пор, пока не увидите проблему, потом вы закрываете отладчик и возвращаетесь в браузер чтобы обновить код, потом пытаетесь запустить код снова.   А почему бы не обновить код прямо в отладчике и продолжить наш веселый путь!?  Еще одна очень полезная техника - это расстановка точек остановки (breakpoints) в проблемных местах кода, потом вы сможете вручную манипулировать живыми объектами чтобы имитировать различные условия, которые помогут вам в отладке (кучи исписанной бумаги с записями значений временных переменных для различных сценариев ушли в прошлое).  Иногда необходимо начать выполнение программы с начала, это зависит от конкретного контекста, но в большинстве случаев вы сможете подправлять код прямо во время отладки.

Вопросы и Ответы

В этом месяце вопросов не будет, я полагаю, весь материал из предыдущей статьи был прост и ясен ;-)

Глоссарий

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

Базовые классы (Base Classes)
        Базовые классы языка Smalltalk с которых вы начинаете новый образ.  Например, Object, Boolean, Magnitude, Stream и т.д.  Хотя вы можете (а иногда и должны) внести свои изменения в базовые классы, делать этого вообще-то не рекомендуется, т.к. можно легко испортить образ или нарушить совместимость с будущими версиями.

Пространство имен (Namespace)
        Пространство имен - это почти то, о чем нам говорит само это словосочетание - конечное пространство, где могут быть определены и идентифицированы имена.  Например, если вы пишете программу, и вы хотели определить класс Object - из этого ничего не выйдет, т.к. класс Object уже существует. Если бы у вас была возможность определить второй класс с именем Object, то как бы компьютер смог их различать?  Во многих версиях Smalltalk есть единое пространство имен (заметное исключение составляет лишь VisualWorks Smalltalk).

Коллизии пространства имен (Namespace collision)
        Когда две компании/группы/два человека пытаются назвать свои классы одинаково или определить методы класса с одинаковыми именами,   для того, чтобы избежать коллизий пространства имен не только в ваших собственных проектах, но и у других компаний, третьих поставщиков, классы обычно именуют, используя в качестве префикса акроним. Например, если вы работаете в MegaCorp, то вы, вероятно, добавите ко всем классам префикс 'Mc'.

Рефакторизация (Refactor)
        Изменение/обновление/реорганизация вашего кода чтобы сделать его (надеюсь ;-) чище, быстрее, понятнее.   Рефакторизация обычно распределяет ответственности методов и/или превращает бОльшие методы в меньшие.
 

Сноски

[1] Файл перепишет, но не удалит, любую существующую структуру/поведение.   Например, если у вас в классе есть метод, который не существует в соответствующем классе для подзагрузки (filein), тогда такой метод все еще будет там и после нее.  С изменяемыми множествами (change sets) все происходит немножко по-другому, и мы обсудим их в будущих статьях, если вам будет интересно.

[2] Технически это не правильно.  Как и во многих других частях этого цикла статей, я все немного упрощаю, чтобы материал был понятен для начинающих. Давайте будем считать для простоты , что Object является корневым объектом (root object) языка Smalltalk.
Точнее, Smalltalk имеет элегантный метод самонастройки своей иерархии классов, которые являются корневыми в мета-классах (meta classes) и циклическое определение.  Если вы не поймете следующее предложение - не беспокойтесь, потому что обычно вам не о чем беспокоиться, если вы используете обычный Smalltalk.  В Squeak действительно есть класс ProtoObject (прото-объект), который является суперклассом (super class) Object, и ProtoObject наследует от нуля (nil) единственный экземпляр UndefinedObject (неопределенный объект), который наследуется от Object.  В других реализациях Smalltalk есть что-то похожее.

 


Copyright © 2001, Jason Steffler.
Copying license http://www.linuxgazette.com/copying.html
Published in Issue 62 of Linux Gazette, February 2001

Вернуться на главную страницу