Bags o' Fun.
Автор: © Jason Steffler
|
Резюме Те, кто не читал предыдущие статьи этой серии, обязательно сначала ознакомьтесь с разделом "Для чего нужны эти статьи?". В этом месяце мы займемся изучением наследования, полиморфизма, абстрактных классов, а также познакомимся с таким объектом как коллекция. Те, кто хочет прочитать предыдущие статьи серии и быть в курсе новостей, связанных с этими статьями, - рекомендую посетить MST page. Те же, кто не хочет ограничиваться данными статьями и жаждет новых знаний - отсылаю вас на http://mimmow.cc.gatech.edu/377, где можно найти список неплохих ресурсов, которые помогут вам в ваших поисках.
Фраза дня Почему Smalltalk? Да потому что Smalltalk - это по сути упрощенная версия английского. Существительные и глаголы. Объекты - это существительные. Глаголы - это сообщения пересылаемые от одного объекта к другому. Это просто, как 2х2. Нет больше необходимости писать загадочные наборы слов и колдовать над кодом. Это значит, что практически каждый может научиться программировать на Smalltalk. - Peter William Lount.
Наследование В прошлой статье мы использовали объект aPerson для иллюстративных целей. В этот раз мы опять остановимся на этом примере, но только ненадолго, потому что он становится уж слишком мудреным. Другая цель, которая оправдывает использование объекта aPerson - это познакомить вас с тем, что такое коллекции объектов в Smalltalk. Вернемся мы к этому объекту и в следующей статье, но не ради самого объекта aPerson, а опять же из иллюстративных целей. Понятие наследования в ООП по смыслу несильно отличается от того, который мы придаем эту слову в повседневной речи. Точно так же, как человек наследует некоторые черты родителей, объекты наследует некоторые свойства родительских объектов. Объекты, помимо всего прочего, наследуют методы и переменные (instance variables) своих родителей. Чтобы на практике ознакомиться с этим понятием, давайте возьмем, к примеру, объект anOrderedCollection класса OrderedCollection. Этот объект достаточно часто используется в Smalltalk. OrderedCollection - это именно то, что указано в названии. А именно - коллекция индексированных объектов. Для лучшего понимания того, как все это можно использовать на практике, давайте обратимся к примерам: [пример 1] (OrderedCollection new) add: 1; add 2; add 3; yourself. [пример 2]
(OrderedCollection new) add: 1; add: 2; add: 3; yourself. В этом примере, мы просим класс (помните? класс - это основа для создания новых объектов) OrderedCollection создать новую коллекцию. Затем мы просим новую коллекцию добавить 1 к самой себе. Следующие шаги - последовательное добавление к той же коллекции 2 и 3, и затем - мы просим коллекцию показать свое содержимое. Как правило, нет необходимости использовать команду yourself для обращения к объекту, потому что данное действие выполняется по умолчанию. То есть сам объект является получателем (receiver) сообщения. Но, с другой стороны, сообщение (message) add: возвращает не содержимое коллекции, а значение передаваемых ей параметров. Именно это и происходит в данном случае. Поэтому, если нам нужно вывести содержимое объекта OrderedCollection, создаваемого нами, нам нужно явно указать это, что мы и сделали с помощью yourself. Такой подход может, наверное, вызвать определенные трудности. Теперь посмотрите на следующие кусочки кода, в которых объект создается неявно (имплицитно), в отличие от предыдущего явного|эксплицитного способа. Результат один и тот же: [пример 3]
OrderedCollection with: 1 with: 2 with: 3 [пример 4]
OrderedCollection withAll: #(1 2 3) [пример 5]
[пример 6]
[пример 7]
Данный пример является иллюстрацией того, каким колоссальным потенциалом обладает возможность манипулирования объектами и просмотра результатов этих манипуляций в реальном времени. Если в процессе программирования у вас возникают какие-то затруднения, то можно прервать исполнение программы, активизировать проблемный объект и получить предельно точную картину того, что с ним происходит. Если вам необходимо смоделировать определенные условия, можно просто напрямую изменить объект в соответствии с вашими нуждами. Например, к вам в голову вдруг пришла идея о том, что вместо элемента коллекции '4' типа Integer, вам нужен элемент 'four' типа string. Кликните на четвертом элементе, удалите '4', введите новое значение 'four', затем кликнете средней кнопкой мыши чтобы осуществить операцию 'accept'. Теперь значение четвертого элемента - 'four'. Попробуйте щелкнуть на третьем элементе, затем вернитесь к четвертому за подтверждением. Вы увидите:
...И запомните - мы проделали все это без компилирования, линкования и запуска скомпилированной программы. Хорошо, теперь у нас уже есть представление о том, как создавать коллекции. Следующее задание - научиться что-нибудь делать с этими коллекциями, к примеру, добавлять элементы типа Integer. Для этого на сделать 'do it' со следующим кусочком кода: [пример 8] | anOrderedCollection aSum | aSum := 0. anOrderedCollection := OrderedCollection withAll: #(1 2 3). anOrderedCollection do: [:anElement | aSum := aSum + anElement]. aSum inspect.Эти строки имеют следующий смысл:
Те из вас, ребята, кто уже имеет какой-то опыт программирования, наверняка обратят внимание на то, что мы не уделяем никакого внимания на проверку ограничений, то есть на размерность коллекции, и не заботимся об объявлении временных переменных для индексирования коллекции - все это решено уже самой коллекцией по определению. Довольно удобно, к тому же позволяет уменьшить количество совершаемых ошибок. Нам достаточно просто попросить коллекцию сделать что-нибудь с каждым элементом через 'do'. Итак, возвращаемся непосредственно к наследованию. Как и подсказывает нам имя 'OrderedCollection', данный класс является модификацией класса 'Collection'. 'OrderedCollection' наследует методы и instance-переменные 'Collection'. Если быть более точным, 'OrderedCollection' наследуем их от класса 'SequenceableCollection', который, в свою очередь, наследует непосредственно от 'Collection'. Сегодня в нашем распоряжении UML и целый арсенал другого программного обеспечения для моделирования диаграмм, но в целях экономии времени я ограничусь рабочей моделью, сделанной просто в plain text: я продемонстрирую взаимосвязь между классами, наследование обозначается символом табуляции: Collection SequenceableCollection OrderedCollection Данную взаимосвязь можно понимать и так: 'OrderedCollection' - это разновидность 'SequenceableCollection', а последний класс - разновидность 'Collection'. Например, метод создания объекта, использованный нами: withAll, наследуется от 'Collection'. Смотрите: Collection >>withAll: SequenceableCollection OrderedCollection И 'Collection', и 'SequenceableCollection' являются примерами того, что мы называем абстрактными классами, то есть такими, которые, будучи неспособными самостоятельно породить объект, служат логическими отправными точками для конструировани яобъктов. Здесь не имеет значения, что у нас - OrderedCollection, SortedCollection, Bag (неиндексированная коллекция) или что-то еще - важно то, что нам нужно, чтобы все эти классы знали, как реагировать на метод withAll:. Вся прелесть в том, что нам для решения поставленной задачи необходимо внести изменения (обработку withAll:) лишь один раз - все производные классы унаследуют эту способость работы с withAll:. Если вам необходимо реализовать возможность исключений из общего правила наследования (например, у вас есть класс Heap2, которому нужно работать с withAll: по-особому), тогда вы, в терминах программирования, осуществите overriding метода withAll: класса Heap. Если добавить класс Heap в нашу схему наследования, то получится следующее: Collection >>withAll: SequenceableCollection OrderedCollection Heap >>withAll: Примечание: когда мы обращаемся к методу withAll: классов Heap или OrderedCollection, то одно и то же обращение (message) реализовано дважды и по-разному - это называется полиморфизмом. Еще один пример из серии загадочных терминов, скрывающих за собой достаточно простые вещи. Из понятия полиморфизма, однако, непосредственно вытекает одно принципиально важное следствие - полиморфизм позволяет перейти от концептуальных рамок процесса принятия решений к новому складу ума: командному. Полиморфизм избавляет разработчика от аспектов программирования, свойственных процедурному программированию - таких как необходимость большого количество кода для проверки различных параметров и условий (например, если это 'OrderedCollection - делай это, если Heap - делай другое, если Bag - делай что-нибудь еще и т.д.). С полиморфизмом же, не имеет никакого значения, с каким типом коллекций вы работаете: при обращении к методу withAll: произойдет именно то, что и должно произойти. Наконец, если мы добавим в нашу схему еще и SortedCollection и Bag (<вздох облегчения> да, именно отсюда название статьи ;-), то получим: Collection >>withAll: Bag SequenceableCollection OrderedCollection SortedCollection Heap >>withAll: Данная схема позволяет наглядно убедиться в открывающихся возможностях повторного использования, имеется ввиду возможность сделать|изменить что-то один раз и таким образом дать целому ряду объектов пользоваться этими модификациями. Таким образом достигается синхронизация. Теперь мы подошли к вопросу о том, каким образом определить где какие объекты находятся и как их использовать. Как и в случае с другими темами в этой серии статей, я буду знакомить вас с одним объектом за раз. Дело в том, что очень распространенной проблемой, с которой сталкиваются начинающие изучать Smalltalk, является то, что у них буквально разбегаются глаза при знакомстве с богатейшей библиотекой классов Smalltalk (количество объектов изчисляется сотнями). Чтобы справиться с этой проблемой, я расширил один из браузеров Smalltalk и создал ScopedBrowser. Это является хорошей иллюстрацией уже упомянутой гибкости: мне удалось изменить работу IDE (интегрированной среды разработки) в соответствии с моим требования. 'ScopedBrowser' позволит нам сконцентрироваться только на тех объектах, которые нам понадобятся в этой статье. Моя задумка заключается в том, чтобы добавоять новые объекты в браузер по мере их изучения. На этот раз, я выбрал все уже упомянутые варианты коллекций плюс еще пару объектов для особо интересующихся (итого: 9 классов). Чтобы открыть этот браузер, сначала нужно добавить код из файла MakingSmalltalk-Article3.st к вашему образу (image) (о том, как это сделать - читайте статью #2 из этой серии). Затем откройте браузер запустив следующий код: [пример 9] [только для Squeak]
Тем из вас, кто просто читает эту статью без запущенного Smalltalk, предлагаю взглянуть на результат:
(Примечание: Я установил фиолетовый цвет браузера, а по умолчанию стоит зеленый. Мы вернемся к вопросу настроек среды разработки в следующих статьях). Чтобы найти метод withAll:, кликните на кнопке class, затем: Collections-Abstract>Collection>instanceCreation Теперь, если мы вернемся немного назад и кликнем на Collection-Sequenceable>OrderedCollection, то увидим:
Обратите внимание на то, что панель с кодом указывает на родительский по отношению к OrderedCollection класс, наряду с instance-переменными. Если вернуться к абстрактным классам и кликнуть на SequenceableCollection, то мы увидим, что этот класс является наследником класса Collection, о чем мы и говорили. Поэкспериментируйте немного с классами в браузере, чтобы привыкнуть к его интерфэйсу. Найдите классы и методы, которые мы разобрали выше.
В заключении, я бы хотел познакомить вас с еще одной разновидностью браузера - иерархической (hierarchy browser). Такой браузер очень хорош, когда работаешь с иерархиями классов. Чтобы открыть его, сначала снова кликните на OrderCollection, затем - [средней кнопкой] spawn hierarchy. Вы увидите:
[пример 10]
Обратите внимание на отсутствие ограничений иерархического спектра - показывается полная иерархия объектов. Заметьте, что Collection является наследником объекта Object. Логично, не так ли? Вообще говоря, весь Smalltalk таков.
Начальной точкой иерархического дерева является объект ProtoObject, который включает в себя самые фундаментальные методы. Возникает естественный вопрос: "А чьим наследником является ProtoObject?" Ответ: ничьим или наследником nil, если быть более точным.
В следующей статье
В следующей статье мы займемся созданием объекта aPerson. Разумеется, мы не ограничимся просто изучением основ создания объектов, но также уделим время разбору|анализу программного кода.
A Sweet Squeak
В этом разделе вы не найдете разборов кода или примеров. Здесь представляются просто какие-то интересные "штучки" для экспериментов. В этот раз мы займемся скачиванием проектов. Помните, как в первой статье мы создавали проект? Дело в том, что возможности не ограничиваются просто сохранением проекта и его передачей в разные образы. Также можно выкладывать проект в Сеть и скачивать его прямо в ваш образ. Вот пример того, как это можно сделать:
[пример 11] [только для Squeak]
Для тех, кто сейчас находится в "режиме" только-чтение - перед вами предстанет проект простой игрушки turtle, после запуска проекта у вас появится возможность управлять черепахой посредством кода.
Вопросы и ответы
Этот раздел предназначен для обсуждения вопросов, которые у вас появились после прочтения предыдущих статей (разумеется, надо учитывать и фактор времени (у меня его немного)). Из всех вопросов для ответов я выбираю те, чье представление лучше вписываетс в логику этой серии статей. Если вам нужен более быстрый ответ и ответ вообще (я могу не ответить на ваш вопрос), попробуйте послать вопрос в группу новостей comp.lang.smalltalk, или же в Swiky.
Вопрос: Насколько совместим код, показыванный в примерах с [VisualWorks, VisualAge, Smalltalk/X, Dolphin и т.д.]?
Ответ: Хотя переносимость кода и является моей первостепенной задачей и я даже не пробовал портировать примеры, основной код должен работать|портироваться без проблем. Под основным кодом я понимаю такие вещи как способы использования коллекций, объявления классов, instance-переменных и т.д. Как правило, разные модификации Smalltalk различаются лишь своим GUI. Что касается Squeak, то он обладает многими уникальными свойствами и возможностями, которые невозможно портировать, например: halo, morphic, скачивание проектов.
В этом направлении (имеется ввиду вопрос портирования) я начал кое-что делать в своих статьях: специфический для Squeak код теперь помечается соответствующим образом. Хотя не забывайте, что эти пометки - это всего лишь мое предположение (у меня просто нет времени на дополнительное тестирование и решение одной и той же задачи разными способами).
В связи с этим, предлагаю использовать возможности раздела 'talkback' Linux Gazette - если кто-то заметит, что что-то (не)работает на каком-то из диалектов Smalltalk - посылайте в этот раздел соответствующую информацию. Также я решил индексировать примеры, начиная с этой статьи (пример 1, пример 2). Первоначально это не входило в мои планы, так как серия статей задумывалась как нечто неформальное, однако в связи с использованием 'talkback' имеет смысл индексирование - так будет понятней и удобней. Если вам не нравится способ индексация - 'talkback' к вашим услугам.
Глоссарий
[абстрактный класс|abstract class] [метод класса|class method] [конкретный класс|concrete class] [метод образца|instance method] [получатель|receiver]
Получатель посланного сообщения. Объект, получающий это сообщение. Например, если мы говорим, что aPerson sing, получателем сообщения sing является объект aPerson
[только для Squeak|Squeak-only-suspected]
Пометка в программном коде примеров из статей, которая значит, что я предполагаю работоспособность кода только на Squeack. Примечание: не забывайте, что это всего лишь мое предположение - я не собираюсь тратить время на тестирование кода на модификациях Smalltalk или пытаться решать одну и ту же проблему разными способами.
Для чего нужны эти статьи?
Когда я написал свою первую статью из серии "Программируем на Smalltalk под Linux" в марте 2000 года, то я ориентировался на аудиторию, состоящую из опытных программистов, которые были плохо знакомы с ООП или Smalltalk. Целью статьи было дать общее представление о моем любимом языке программирования и его использовании под моей любимой операционной системой. С того времени, ко мне поступило большое количество просьб ответить на вопросы касательно программирования на Smalltalk и ООП вообще для начинающих. Тогда я и решил попробовать написать небольшую серию статей такого рода.
Предполагаемая аудитория этой серии - люди, начинающие свое знакомство с миром ООП и программирования вообще. Цель статей - не просто рассказать об ООП, но и показать, каким увлекательным может быть программирование на Smalltalk. Возникает вопрос: "А зачем все это нужно, когда Сеть просто кишит такого рода документацией. Существует две главных причины: 1) Tutorials - это, конечно, просто здорово, но там материал часто представлен в застывшем статическом виде и информация быстро устаревает. 2) Моя серия претендует на то, чтобы быть более захватывающей и удобоваримой, чем Tutorials.
Что касается второго аргумента, то я пишу статьи таким образом, чтобы их можно было осилить меньше чем за час. Надеюсь, новички по мере чтения будут возвращаться к предыдущим статьям для разъяснений и более глубокого понимания материала. Также у меня в планах периодически добавлять в статьи чего-нибудь остренького - из продвинутого материала. Как и раньше, со статьями можно будет работать, как непосредственно работая в Smalltalk, так и просто читая их.
Почему Smalltalk?
Я убежден в том, что Smalltalk - это лучшая среда для изучения ООП, потому что:
Код Smalltalk
В этот раз без листингов.
|
Copyright © 2000, Jason Steffler. Copying license http://www.linuxgazette.com/copying.html Published in Issue 61 of Linux Gazette, January 2001 |
Вернуться на главную страницу |