- •1 КОНЦЕПЦИИ ЯЗЫКА SMALLTALK-80
- •2 ОПИСАНИЕ ЯЫКА SQUEAK SMALLTALK
- •2.1 Основные понятия
- •2.2 Синтаксис языка Smalltalk
- •2.3 Описание классов объектов
- •3 ПАРАЛЛЕЛЬНАЯ РАБОТА
- •3.1 Объекты класса Process
- •3.2 Объект класса ProcessorScheduler
- •3.3 Объекты класса Semaphore
- •5.1 Децентрализованное управление процессами
- •5.2 Централизованное управление процессами
- •5.3 Разработка моделей систем с дискретными событиями
- •5.4 Протоколирование результатов моделирования
- •5.5 Учебные задачи моделирования
- •5.5.1 Обработка партии деталей на двух станках
- •5.5.2 Грузоперевозки автотранспортом
- •5.5.3 Управление посадкой самолетов
- •5.5.4 Исследование характеристик систем массового обслуживания
- •Библиографический список
2 ОПИСАНИЕ ЯЫКА SQUEAK SMALLTALK
2.1 Основные понятия
Объект это совокупность данных и методов манипулирования ими, напри мер, целое число 25 и операции сложения, умножения и др. Простые объекты – числа, байты и символы являются встроенными единицами языка программиро вания. Сложные объекты строятся комбинированием простых и других слож ных объектов. Каждый объект принадлежит какому-нибудь классу [2].
Класс – описатель структуры и функционирования объекта или абстракт ный тип данных, описывающий состав и поведение каждого объекта данного типа. Чтобы создать конкретный объект, классу (который тоже является объек том) надо послать сообщение new (новый экземпляр). Экземпляр класса и объект далее понимаются как эквивалентные понятия. Возможности манипули рования объектами определяются доступными методами. Метод – программа, реализующая некоторую функцию, определенную над объектами данного клас са. Сообщение – вызов метода. Имя сообщения – имя конкретного метода, например,
openFile: 'test.txt'
в классе File может открыть файл с указанным именем. Программы на языке Squeak Smalltalk пишутся в редакторах, входящих в
систему программирования. Среда управляется меню и клавиатурными ко мандами. Для исполнения программы в среде Squeak необходимо выделить фрагмент текста клавишами или мышкой и запустить интерпретатор командой меню "Do it!" или "Print it!". В первом варианте будет исполнен выделенный фрагмент без отображения результата, во втором с отображением результата в рабочем окне.
Весь синтаксис в языке Smalltalk сводится к посылке объекту сообщения в
виде
<объект> <сообщение>,
например, 5 factorial (сообщение factorial посылается объекту 5). Поскольку объект 5 – целое число, то в классе Integer ищется и выполняется метод экзем пляра с именем factorial. В Smalltalk существуют сотни классов и тысячи мето дов. Все они имеют имена (часто довольно длинные), отражающие их семанти ку. Например,
'Привет' outputToPrinter
6
выводит строку 'Привет' на принтер. Это значительно улучшает читаемость про грамм. Синтаксис языка конструировался таким образом, чтобы тексты про грамм были максимально приближены к предложениям английского языка. На сколько это удалось реализовать читатель может судить сам.
Сообщения различаются своей формой, отражающей наличие и состав па раметров. Унарное сообщение – сообщение без аргументов, имя которого со стоит из одного слова – имени вызываемого метода, например, 5 factorial.
Бинарное сообщение – сообщение с одним аргументом, имя которого со стоит из одного или нескольких специальных символов, например, 5 + 10 (сооб щение "+" с аргументом 10 посылается объекту 5).
Составное сообщение – сообщение с одним или несколькими аргументами, имена которых состоят из слов, оканчивающихся двоеточием, например,
DiskC openFile: 'tmp' for: #write inPath: 'user'.
Составные сообщения напоминают вызов параметров с ключевыми слова ми, после которых следуют их значения.
Выражение – последовательность сообщений, заканчивающаяся точкой. Далее читателю предстоит внимательно различать точки языка Smalltalk и точ ки, заканчивающие предложения данного текста. Порядок вычисления выраже ний следующий: сначала вычисляются выражения в круглых скобках, затем по следовательно – унарные, бинарные и составные сообщения. Вычисление сооб щений одинакового приоритета ведется слева направо. Результатом вычисления выражения 2+4*6 будет целое число 36. Любой метод всегда возвращает объект. Результатом вычисления выражения всегда является объект, возвращен ный ПОСЛЕДНИМ ВЫПОЛНЕННЫМ МЕТОДОМ.
Имена классов, переменных и методов – идентификаторы, используемые для ссылок на объекты. При написании имен важное значение имеет регистр: строчные и прописные буквы считаются РАЗНЫМИ. Хотя имена в Smalltalk не должны содержать символов кириллицы, в примерах для удобства восприятия будут использоваться русские слова.
В языке существуют пять типов переменных, различающихся временем своего существования и областью действия:
∙временные переменные;
∙переменные экземпляра;
∙переменные класса;
∙глобальные переменные;
∙общие переменные.
Временные переменные объявляются внутри методов или программ, созда ются в момент вызова метода и уничтожаются по возвращению из метода. Син таксическим признаком объявления временных переменных являются символы
7
«|» – вертикальная черта, начинающая и заканчивающая список временных переменных программы или метода, например:
| name count total |
Область видимости временных переменных ограничена программным фрагментом, где они определены.
Переменные экземпляра определяются при описании класса и хранятся в памяти внутри каждого экземпляра объекта. Ссылки на них допускаются только внутри методов экземпляра. Они существуют до тех пор, пока существует эк земпляр. Для доступа прикладных программ к таким переменным необходимо в соответствующем классе предусмотреть описание интерфейсных методов запи си и чтения значений. Прямой доступ к переменным экземпляра из программ запрещен. Это бы приводило к нарушению принципа инкапсуляции объектноориентированного программирования.
К переменным класса разрешен доступ из методов класса и экземпляра. К глобальным и общим переменным разрешен доступ из любого программного фрагмента: главной программе, метода класса и экземпляра. Переменные клас са, глобальные и общие зарегистрированы в общих словарях. Переменные клас са регистрируются в словаре по имени classPool, глобальные переменные за регистрированы в словаре по имени Smalltalk, общие переменные зарегистриро ваны в словаре, объявленном специальным образом.
Переменные класса доступны любому методу класса или методу экземпля ра этого класса для чтения и записи. Глобальные переменные доступны любому объекту для чтения и записи. Отличительным признаком этих переменных яв ляется заглавная первая буква в имени.
Для чтения и записи в общие переменные в описании класса имена слова рей, содержащих доступные для объектов этого класса общие переменные, должны быть объявлены в ключевом слове poolDictionaries.
Имена могут быть глобальными и локальными. Локальные имена суще ствуют только на протяжении выполнения метода, в котором они определены. Локальные имена начинаются со строчной буквы. Глобальные имена существу ют постоянно и начинаются с прописной буквы.
Имена переменных экземпляра доступны только в методах экземпляра, определенных в данном классе, и используются для ссылок на составляющие элементы объектов этого класса. Это названия полей в структуре объекта. Име на переменных экземпляра начинаются со строчной буквы. Например, в классе Прямоугольник определены две переменные экземпляра – верхнийЛевыйУгол и нижнийПравыйУгол. Это означает, что объект прямоугольник состоит из двух объектов. Фактически это должны быть экземпляры класса Точка. Точки в свою очередь состоят из двух целых чисел – x и y. Таким образом, физически, в памя ти, каждый объект – это набор ссылок на другие объекты. Такой подход обу
8
словливает значительную экономию памяти и возможность автоматической "сборки мусора" в памяти при отсутствии ссылок на данные. В языке существу ют также индексированные переменные экземпляра. Например, Array –индекси руемый объект. Если объект состоит, скажем, из четырех, а в классе указана одна именованная переменная, то остальные три являются индексированными, и доступ к ним осуществляется путем посылки объекту сообщения at: ин декс, а не простым указанием имени.
Имена переменных класса – глобальные имена, отличающиеся тем, что они определены только в данном классе и доступны только из методов данного класса.
Присваивание – указание, на какой объект ссылается данная переменная (точнее, ее имя), например:
count : = 1550.
pointA: = pointB + pointC.
Существует несколько классов, объекты которых могут представляться в программах явно, без использования переменных (литералы). В классе Number литералами являются константы:
10 – целое число в десятичной системе счисления; 16r5A – число в шестнадцатеричной системе счисления;
8е1 – вещественное число в десятичной системе счисления;; 8r777 – число в восьмеричной системе счисления.
Литералы имеют классы String ('привет'), Array (#(5 'привет' 16r5A) – мас сив из трех элементов), Character ($A; $#) и Block ([<тело блока>]).
Классы связаны отношением наследования суперкласс/подкласс. Подкласс наследует свойства и методы своего суперкласса. Например, String наследует свойства ArrayedCollection, поскольку строка – это массив символов. Arrayed Collectionв свою очередь наследует от SequenceableCollection, ибо он – разно видность набора; при этом String, как следствие, тоже наследует от Sequence ableCollection (иерархическое наследование). Класс не может иметь более одно го непосредственного суперкласса (в языке нет множественного наследования). Наследуются свойства, методы, имена переменных класса и экземпляра. Мето ды, которые наследовать нежелательно из-за неудовлетворительного выполне ния требуемых функций, могут быть переопределены у объектов потомков.
Процесс разработки программ на языке Smalltalk заключается в програм мировании классов, т.е. описании типов объектов. Запрограммировать класс – это:
∙указать, чьим подклассом он является;
∙указать имена переменных класса;
∙указать имена переменных экземпляра;
9
∙определить (запрограммировать) методы класса;
∙определить (запрограммировать) методы экземпляра и таким образом
специфицировать поведение объектов данного класса.
Некоторые фрагменты программы тоже могут быть объектами, их удобно иногда задавать явно, например, для организации циклов. Они выделяются квадратными скобками и называются блоками, например:
[:count | count factorial – 1 ].
Здесь count – временная переменная блока, значение которой присваива ется в зависимости от контекста, а телом является вычисление факториала.
Выполнение Smalltalk-программы – это последовательное выполнение посылок сообщений объектам. Во время выполнения интерпретатор определя ет, какой объект получил то или иное сообщение. В классе этого объекта ищет ся метод, соответствующий данному имени сообщения. Если метод найден, то он выполняется, если нет – он ищется в суперклассе, и далее по иерархии до самого старшего класса Object, описывающего общие свойства всех объектов (например, свойство иметь размер). Если имя сообщения не найдено и там, фиксируется ошибка. Это самый распространенный и едва ли не единственный тип ошибок в Smalltalk-программах.
Сообщения различают по числу их аргументов. Если сообщение не имеет параметров (унарное сообщение), то идентификатор задает имя вызываемого метода для объекта получателя. Если получателем является имя класса, то вы зывается метод класса, иначе вызывается метод для экземпляра, например:
Set new – метод класса – создание нового экземпляра класса Set; x sin – метод экземпляра – вычисление значения sin(x);
5 factorial – метод экземпляра – вычисление факториала.
Если сообщение используется для обозначения бинарных операций, то его вид следующий
<объект> <имя> <параметр>.
Примерами таких сообщений являются:
5 + b – объекту 5 посылается бинарное сообщение "+ b"; a >= b – объекту a посылается бинарное сообщение ">= b".
В случае, когда метод имеет несколько параметров, тогда используется со ставное сообщение с ключевыми словами:
<объект> <ключ>: <параметр> <ключ>: <параметр> ...
10
Здесь пара <ключ>: <параметр> определяет название и передаваемое зна чение параметра. Набор ключей в составном сообщении определяет имя мето да, вызываемого для объекта.
Так как вызов метода всегда возвращает объект, ему можно направить но вое сообщение. Таким образом, формируются цепочки сообщений. В том слу чае, если одному и тому же объекту необходимо послать несколько сообщений, используется каскадная конструкция сцепленных сообщений вида:
<объект> <сообщение 1>; <сообщение 2>;
. . .
<сообщение n>.
Здесь разные сообщения отделяются друг от друга символом ";" и все они направляются одному и тому же получателю <объект>.
В языке предопределены несколько псевдопеременных, указывающих на объекты. К их числу относятся true, false, nil, self, super.
true (истина) и false (ложь) являются единственными экземплярами (константы) соответствующих классов True и False.
nil (ничего) используется для обозначения отсутствующего значения чего либо.
self (сам) – элемент языка, используемый для обозначения самого объек та получателя сообщения в методе (объект, для которого вызван метод). Часто для выполнения какого-либо метода объекту приходится посылать сообщения самому себе или о самом себе.
super (супер) – элемент языка, дающий неочевидное, но иногда очень по лезное средство использовать метод суперкласса вместо метода, переопреде ленного в своем классе. Отличается от self только тем, что метод ищется, на чиная с ближайшего суперкласса.
Мы рассмотрели практически все основные особенности языка Smalltalk. Перечисленные понятия – необходимый и достаточный минимум для програм мирования на языке Smalltalk. Следует отметить отсутствие в языке конструк ций для организации циклов и проверки условий. Дело в том, что модель объект-сообщение достаточна для организации подобных конструкций базовы ми средствами языка.
Для организации условных конструкций в системе есть классы True и False, имеющие единственные экземпляры констант true и false (обра тите внимание на отличия в записи). В обоих классах определено сообщение ifTrue: с блоком в качестве аргумента. Соответствующий метод в классе True просто выполняет этот блок, а в классе False – ничего не делает. Такое сообще ние обычно посылается объекту – результату вычисления некоторого логиче
11
ского выражения (в частности – просто результату посылки какого-либо сооб щения), например:
(numberA < 100) ifTrue: [Terminal ring]
заставляет терминал пищать, если numberA меньше 100. Круглые скобки здесь необязательны, они улучшают восприятие фрагмента.
Предположим, перед окончанием работы вы хотите отправить нескольким адресатам сообщение типа
'До свидания! Ю.К.'.
Допустим, этих адресатов четыре – два удаленных терминала (ваш компьютер включен в сеть), окно системной информации на вашем экране и файл почты. Пусть все эти адресаты – уже существующие в системе объекты разных классов. Если их нет, то их надо предварительно создать.
Первое, что вы делаете, – создаете новый объект – множество объектов-ад ресатов. Поскольку вы захотите, чтобы этот новый объект "жил долго", вы заве дете для него глобальную переменную:
Addreses := Set new.
Компилятор после уточняющего диалога о намерении создать глобальную переменную включит Адресаты в число глобальных переменных. Теперь вы за полняете вновь созданное множество нужными объектами:
Addreses add: RemoteTerminal_1.
Затем, аналогично, ФайлПочты и т.д. Теперь программа может иметь следую щий вид (вариант 1):
| message |
message := '17:30 До свидания! Ю.К.'. Addreses do: [ :suit | suit send: message].
Временные переменные (здесь одна – message) указываются в вертикаль ных черточках. Метод do: (для всех элементов выполнить) определен в классе Collection и наследуется классом Set (Множество). Он выполняет задан ный аргументом блок столько раз, сколько элементов содержится в наборе. Каждый раз временной переменной "suit" последовательно присваивается очередной элемента этого набора. Метод send: должен быть определен во всех нужных нам классах. Методы разных классов выполняют, по сути, одну и
12
ту же функцию, но делают это, разумеется, по-разному: одно дело ввести строку в окно, и совсем другое – отправить ее в коммуникационный порт. Какие мето ды и когда будут работать – определится динамически, во время выполнения программы, в зависимости от того, какому классу будет принадлежать обраба тываемый в данный момент объект (полиморфизм ООП).
Для того, чтобы пользоваться этой программой в дальнейшем и сделать ее применимой для разных строк передаваемого текста, следующий метод можно определить в классе String (вариант 2).
sendAll
"Отправить всем элементам множества Addreses строкуполучатель в качестве сообщения"
Addreses do: [ :suit | suit send: self ].
В кавычках дан комментарий. Теперь вы сможете пользоваться этим мето дом для посылки разных строк, например,
'Привет!' sendAll .
Эта программа иллюстрирует замечательное преимущество динамического связывания. Предположим, вы хотите выводить вашу строку еще и на принтер. Все, что надо сделать, это выполнить
Addreses add: Printer.
и убедиться в наличии метода send: в классе принтеров. Нет необходимости что-либо менять в программах. Затраты на модификацию минимальны.
Метод sendAll не отражает преимуществ наследования. Поэтому приве дем еще один пример.
Предположим, у вас разработана система моделирования транспортного движения в городе. Имеются классы типа Улица, Перекресток, Светофор и т.п. и, конечно, Автомобиль. У класса Автомобиль, очевидно, есть подклассы: Лег ковой, Автоцистерна и т.п. Теперь вам понадобилось внести в работу системы снегоуборочную машину. Вы определяете класс СнегоуборочнаяМашина как подкласс класса Автомобиль и программируете только специфические для нее свойства – в остальном она будет работать так же, как и все остальные автомо били. Не надо заботиться и о выделении памяти: как объект машина наследует от класса Object соответствующие методы. Перепрограммирование других классов системы также не требуется.
13
