В ходе развития методов создания совершенного программного продукта постепенно возникают новые методологии его построения, которые значительно увеличивают производительность труда программиста, облегчают и дисциплинируют производственные процессы проектирования, модернизации и сопровождения больших прикладных программ. Среди них выделяется такое ныне популярное направление, как объектно-ориентированная методология программирования.
Особую роль в популярности этого подхода сыграла его тесная связь с интерфейсами пользователя (особенно графическими), а также включение элементов этой идеологии в популярные сегодня (на персональных компьютерах фирмы IBM) языки программирования C++ и Pascal with Objects фирмы Borland (эти языки некоторые авторы называют "гибридными"). Распространение мощных и доступных по цене персональных компьютеров дало техническую основу для их широкого распространения и применения в программисткой практике.
За последние пятилетие в США, Японии и Западной Европе значительно вырос интерес к "чистому" объектно-ориентированному языку программирования - языку Smalltalk. Вероятно самое первое издание, посвященное этому языку - августовский выпуск журнала "BYTE" в 1981 году, на обложке которого впервые появился воздушный шар с именем Smalltalk на борту, резво летящий от уединенного острова "общих исследований" к высотам популярности! Сегодня, число ежегодно издающихся книг по объектно-ориентированной методологии программирования и языку Smalltalk там переваливает за сотню. Только в России и теоретические исследования и программистская практика, связанные с этим направлением, широким кругам программистов практически не известны. Книги на русском языке, хотя бы едва-едва затрагивающие эти вопросы, можно пересчитать на пальцах одной руки.
В море англоязычных книг, посвященных объектно-ориентированной методологии
программирования и языку Smalltalk, выделяется
книга, первое издание которой получило "литературный псевдоним" -
Голубая Книга (Blue Book). В нашей статье речь пойдет о втором издании
этой книги, книги написанной членами группы разработчиков системы Smalltalk-80
Книга уникальна уже потому, что предлагает исчерпывающее описание объектно-ориентированной идеологии программирования (часть 1), содержит подробное описание реализации этих идей в объектно-ориентированном языке программирования Smalltalk-80 (часть 2), дает примеры его эффективного использования при создании объектов, имитирующих поведение моделей, управляемых событиями (часть 3). По своему содержанию книга интересна и для тех, кто хотел бы познакомиться только с объектно-ориентированной парадигмой программирования, и для тех, кто использует эту идеологию, независимо от того каким языком программирования - "чистым" или "нечистым" - он пользуется, и для тех, кто сегодня читает лекции и ведет практические занятия по современным языкам программирования в вузе, лицее или школе. Книга, при соответствующем уровне математической и программистской культуры, доступна учащимся старших классов средней школы и студентам младших курсов вуза, за исключением, может быть, третьей части, для понимания которой необходимо прослушать хотя бы небольшой курс по основам теории вероятности и математической статистики.
Но книга практически недоступна! Англоязычное издание, или перевод на ряд европейских языков, в России не купить и в библиотеках не найти. Ее перевод на русский язык, сделанный авторами этой статьи, которые по ходу перевода, разработали и читают курсы лекций по данной тематике на механико-математическом факультете Ростовского государственного университета, более года не находит в России издателя. Цель данной публикации - рассказать о том, что у нас пока мало известно, найти единомышленников, и, очень хочется надеяться, найти спонсоров и издателя Голубой Книги (пока почти что сказки) программирования!
Попытаемся, перелистывая страницы Голубой Книги, рассказать что же такое Smalltalk, и, одновременно, рассказать немного о содержании самой книги.
"Книга - это зеркало. Если в него смотрится обезьяна, то из него не может выглянуть лик апостола."
Система Smalltalk-80,
начало создания которой формой Xerox из городка Пало Альто, Калифорния,
США, восходит к 1970 году, основывается на идеях алгоритмического языка
Simula (Симула), и на идеях
Созданный на этой идеологии в начале восьмидесятых Smalltalk-80 характеризуется следующими чертами:
Объект - компонент системы. Объектами, например, являются: числа, строки символов, очереди, словари, прямоугольники, каталоги файлов, редакторы текстов, программы, компиляторы, вычислительные процессы, финансовые отчеты, визуальные представления информации ...
Каждый объект состоит из некоторой принадлежащей ему памяти и множества операций, таким образом объединяя в себе (инкапсулируя) и данные и операции по их обработке. Природа операций над объектом зависит от того, что представляет объект. Объекты, представляющие числа, используют арифметические функции. Объекты, представляющие структуры данных, хранят и обрабатывают информацию.
Сообщение - запрос к объекту выполнить одну из операций. Сообщение определяет, какая требуется операция, но не определяет как эта операция должна выполняться. Только получатель - объект, которому послано сообщение, определяет, как выполнять требуемую операцию. Выполняемая операция, если она возможна, рассматривается как встроенная способность объекта, которую можно единообразно вызвать посылкой ему сообщения.
Множество всех сообщений, на которые может отвечать объект, называется интерфейсом объекта с остальной частью системы. Воздействовать на объект можно только через его интерфейс. Сообщения обеспечивают модульность системы, так как они задают тип требуемой операции. Для взаимодействия с объектом необходимо лишь знать, какие сообщения он может принимать, но не нужно знать, как объект устроен и как он будет выполнять полученное сообщение.
Класс описывает реализацию множества объектов, которые представляют подобные компоненты системы. Отдельные объекты, описываемые классом, называются его экземплярами. Именно класс описывает структуру памяти своих экземпляров, которая представляется множеством переменных, и то, как экземпляры выполняют посланные им сообщения, то есть описывают методы, определяющие как выполнить операцию, затребованную соответствующим сообщением.
Каждый класс имеет имя, которое описывает тип компонента системы, представляемый его экземплярами. Например, класс, экземпляры которого представляют последовательности символов, называется String (Строка); класс, экземпляры которого представляют положение на плоскости, называется Point (Точка); класс, экземпляры которого представляют прямоугольные области, называется Rectangle (Прямоугольник); класс, экземпляры которого представляют вычислительные процессы, называется Process (Процесс).
Каждая переменная в принадлежащей объекту памяти ссылается на некоторый объект, называемый значением этой переменной. Большинство таких переменных имеют имена. Объекты, к которым можно получить доступ из конкретного места программы, определяются через доступные имена переменных. Имя переменной - простой идентификатор, то есть последовательность букв и цифр, начинающаяся с буквы. Например,
index bin14Total initialIndex HouseholdFinances textEditor Rectangle bin14 IncomeReasonsМетод может сделать некоторые изменения в собственной памяти объекта и/или послать другие сообщения. Метод также определяет объект, который должен быть возвращен как значение вызвавшего метод сообщения. Определенным в классе методам доступны пять различных видов переменных. Эти виды переменных различаются по широте доступа к ним и по времени их существования в системе.
Существуют два вида частных переменных, доступных только одному объекту.
Три других вида переменных доступны более чем одному объекту и называются общими переменными. Они различаются по тому, насколько широко они доступны объектам системы.
Имена общих переменных (в том числе и имена классов!) пишутся с прописной буквы, в то время как имена частных переменных - со строчной. Значение общей переменной не зависит от того, какой объект системы использует метод, в котором присутствует ее имя. Значение переменных экземпляра и временных переменных зависит от экземпляра, использующего метод.
Программирование в системе Smalltalk состоит из создания классов, экземпляров классов и последовательности сообщений, которыми обмениваются эти объекты!
Структура класса, как она описывалась до сих пор, ничего не говорила о каких-либо пересечениях между классами.
Отсутствие пересечений между классами - существенное ограничение на разработку в объектно-ориентированной системы. Но два объекта системы могут быть в основном похожи, отличаясь некоторыми деталями. Например, упорядоченные и неупорядоченные наборы похожи в том, что они представляют группы элементов, в которые можно добавлять и из которых можно удалять элементы, но они различаются по способу доступа к элементам. Различие между подобными объектами может быть заметно внешне, например, по тому, что они умеют отвечать на различные сообщения. Различие может быть и чисто внутренним и проявляться в том, что в ответ на одни и те же сообщения похожие объекты выполняют различные методы. Если классам не разрешено пересекаться, то возможность такого частичного подобия не может быть предоставлена системой.
Способ обойти вышеуказанное ограничение - разрешить некоторое пересечения между классами. Мы называем такой подход наследованием. В языке Smalltalk наследование допускает такую ситуацию, при которой одному классу разрешается содержать в себе все экземпляры другого класса, которые ``наследуют'' от первого структуру его экземпляров и их поведение. В такой ситуации мы будем говорить, что один класс является подклассом другого класса, который для первого является суперклассом.
В языке Smalltalk программист всегда создает новый класс как подкласс уже существующего класса. Системный класс Object (Объект) описывает общие свойства всех объектов системы, поэтому любой класс системы является как минимум подклассом класса Object. Описание класса явно указывает его суперкласс, и то, чем его экземпляры отличаются от экземпляров суперкласса. Подкласс тоже класс, следовательно, он сам может иметь подклассы. Каждый класс имеет один суперкласс, в то время как несколько классов могут быть подклассами одного и того же суперкласса; таким образом, структура классов образует дерево, которое определяет иерархию классов. Класс связан с последовательностью классов, от которых он наследует переменные и методы. Эта цепочка наследования начинается с его непосредственного суперкласса и заканчивается классом Object. Object - единственный корневой класс системы; он один не имеет суперкласса.
Приведем некоторую часть иерархии классов системы Smalltalk-80
(отступ обозначает подкласс).
Object Stream Magnitude PositionabIe5tream Character ReadStream Date WriteStream Time ReadWriteStream Number ExternalStrea, Float FileStream Fraction Random Integer Boolean LargeNegativelnteger False LargePositivelnteger True SmallInteger ProcessorScheduler LookupKey Delay Association SharedQueue Collection Behavior SequenceableCollection ClassDsscription LinkedList Class Semaphore MetaClass ArrayedCollection BitBlt Array CharacterScanner WordArray Pen DisplayBitmap DisplayObject RunArray DisplayMedium String Form Symbol Cursor Text DisplayScreen ByteArray InfiniteForm Interval OpaqueForm OrderedCollection Path SortedCollection Arc Bag Circle MappedCollection Curve Dictionary Line IdentityDictionary LinearFit Point Spline Rectangle UndefinedObject
Иерархия классов используется еще и тогда, когда надо определить тот метод, который должен выполнить объект в ответ на полученное им сообщение. Когда объекту посылается сообщение, метод с подходящим именем ищется в классе, которому принадлежит получатель сообщения. Если метод там не найден, поиск продолжается в суперклассе и так далее по цепочке суперклассов снизу вверх, до класса Object, пока нужный метод не будет найден. Если нужный метод не найден ни в одном классе, получателю посылается сообщение с именем doesNotUnderstand: и аргументом, содержащим нереализованное сообщение. В классе Object есть специальный метод с таким именем, который информирует программиста о возникшей ошибочной ситуации.
Поскольку все компоненты системы Smalltalk-80 представляются объектами, а все объекты являются экземплярами классов, сами классы тоже должны представляться экземплярами некоторых классов. Класс, экземпляры которого сами являются классами называется метаклассом. Таким образом, каждый класс является единственным экземпляром своего собственного метакласса и, всякий раз, когда создается новый класс, для него автоматически создается новый метакласс. Как и другие классы, метакласс наследует информацию от суперкласса. Иерархия метаклассов образуется параллельно иерархии классов. Метаклассы похожи на другие классы, поскольку они содержат методы, используемые своими экземплярами.
Но метаклассы отличаются от других классов тем, что они не являются экземплярами своих метаклассов. Вместо этого, они все являются экземплярами класса с именем Metaclass (Метакласс). Кроме того, у метаклассов нет имен. Метакласс становится доступным, когда его экземпляру посылается сообщения class. Например, на метакласс класса Rectangle (Прямоугольник) можно сослаться выражением Rectangle class.
Когда метаклассы были добавлены в систему Smalltalk, был сделан следующий и последний шаг в организации классов. В системе определили абстрактный класс с именем ClassDescription (ОписаниеКласса) для описания классов и их экземпляров. Классы Class и Metaclass сделали подклассами класса СlassDescription. Как мы знаем, цепочка суперклассов любого класса заканчивается на классе Object, а сам класс Object не имеет суперкласса. А метакласс класса Object имеет своим суперклассом класс Class!
Сообщения метаклассам обычно посылаются для создания и инициализации экземпляров, а также для инициализации переменных класса.
Льюис Кэррол.
Синтаксис объясняет, какие последовательности символов составляют правильные выражения. В языке программирования Smalltalk-80 существуют четыре типа выражений.
Из четырех типов перечисленных выше выражений только имена переменных зависят от контекста. Местонахождение выражения в системе определяет, какие последовательности символов будут правильными именами переменных. Множество имен переменных, допустимых в выражениях методов, зависят от класса, в котором находится метод. В остальном синтаксис выражения не зависит от местоположения последнего.
На пять видов объектов можно ссылаться с помощью литеральных выражений. Так как значение литерального выражения всегда один и тот же объект, такое выражение называют литеральной константой: числа, отдельные символы, строки символов, имена, массивы других литеральных констант.
Числа - объекты системы, которые представляют числовые значения и отвечают на сообщения, вычисляющие математические результаты. Литеральное представление десятичного числа стандартное. Например:
3 30.45 -3 -14.0 13772Числовые константы могут выражаться и не в десятичной системе счисления с помощью предшествующего цифрам префикса основания системы счисления. Префикс состоит из цифровой величины основания (всегда выраженной в десятичной системе) и следующей за ней буквы "r". Следующие примеры представляют числа в восьмеричной системе и шестнадцатеричной системе (в скобках даны их десятичная форма):
8r377 (255) 8r34.1 (28.125) 8r-37 (-31) 16rFF (255) 16rAC.DC (172.859)Числовые константы также могут выражаться в экспоненциальной форме с помощью экспоненциального суффикса. Экспоненциальный суффикс состоит из (латинской) буквы "е" и следующего за ним порядка, выраженного в десятичной системе счисления. Число, определенное перед экспоненциальным суффиксом, умножается на основание системы счисления, возведенное в степень, заданную порядком.
1.586e5 (158600.0) 1.586e-3 (0.001586) 8r3e2 (192)
Символы - объекты, которые представляют собой отдельные элементы алфавита. Символьное литеральное выражение состоит из символа доллара ("$") и следующего за ним произвольного символа алфавита. Например:
$t $Ф $- $$ $1
Строки - это объекты, которые представляют собой последовательности символов. Литеральное представление строки - последовательность символов, заключенная в апострофы:
'привет' 'food' 'Система Smalltalk-80'
Любой символ может быть включен в строку. Если апостроф сам входит в строку, то он должен дублироваться, чтобы избежать путаницы с ограничителем. Например, строковая литеральная константа
'can''t'представляет строку из пяти символов $c, $a, $n, $', $t.
#bill #М63Есть некоторые ограничения на символы, которые могут быть в именах. Не существует разных имен с одинаковым набором символов; в системе каждое имя уникально.
#('food' 'utilities' 'rent' 'household' 'trasnport' 'taxes' 'recreation')
Массив из двух массивов и двух чисел описывается выражением
#(('one' 1) ('not' 'negative') 0 -1)
quantity <- 19
theta sinполучатель сообщения - число, на которое ссылается переменная с именем theta, а имя сообщения - sin. Получатель сам решает, как ответить на сообщение, в данном случае, как вычислить функцию синуса от своего значения.
В сообщении previousTotal + increment, имя сообщения - символ "+". Сообщение просит получателя вычислить сумму. В дополнение к имени, сообщение содержит еще один объект - increment, определяющий прибавляемое значение. Дополнительные объекты в сообщении - его аргументы.
По своей структуре сообщения делятся на три группы: унарные, ключевые, бинарные.
Унарные сообщения - сообщения без аргументов, которое вовлекают в операцию только один объект - получатель сообщения. Имя унарного сообщения может быть любым простым идентификатором. Например,
theta sin quantity sqrt namestring sizeКлючевые сообщения - это общий тип сообщения с одним или более аргументами. Имя ключевого сообщения составляется из одного или более ключевых слов, по одному перед каждым аргументом. Ключевое слово - идентификатор с последующим двоеточием. Примеры ключевых сообщений:
index max: limit ages at: 'Brett Jorgensen' put: 3Когда на имя многоаргументного ключевого сообщения ссылаются независимо от получателя этого сообщения, ключевые слова сливаются воедино. Так, например, имя последнего сообщения - at:put:. Число ключевых слов в сообщении может быть любым, но большинство сообщений в системе имеют их не более трех.
Бинарные сообщения - специальный тип сообщения с одним аргументом. Имя бинарного сообщения состоит из одного или двух не алфавитно-цифровых символов. Единственное ограничение здесь то, что второй символ не может быть знаком минус. Бинарные сообщения обычно используются для арифметических и логических операций. Например,
total - 1 total <= maxСообщения в языке Smalltalk обеспечивают двустороннюю связь. Имя и аргументы сообщения передают получателю информацию о том, какой тип ответа нужно подготовить. Получатель передает информацию обратно, возвращая отправителю объект, который становится значением выражения посылки сообщения. Если выражение включает префикс присваивания, то объект, возвращенный получателем, станет новым объектом, на который будет ссылаться переменная. Например, выражение
sum <- 3 + 4устанавливает, что число 7 будет новым значением переменной с именем sum.
Когда в одном выражении встречается несколько сообщений, они выполняются слева направо, при этом имеют место следующие правила синтаксического разбора:
OrderedCollection new add: 1; add: 2; add: 3Здесь три сообщения add: посылаются результату выражения OrderedCollection new. Без каскадирования для получения того же самого результата потребовалось бы четыре выражения (выражения друг от друга отделяются точкой) и переменная:
|temp| temp <- OrderedCollection new. temp add: 1. temp add: 2. temp add: 3
[index <- index + 1] или [index <- index + 1. array at: index put: 0]Когда блок встречается в выражении, заключенные в квадратные скобки выражения блока сразу не выполняются. Последовательность действий, которую описывает блок, будет выполнена только тогда, когда блок получит унарноe сообщение value (значение). Например, следующие два выражения дадут один и тот же результат:
index <- index + 1 и [index <- index + 1] valueОбъект, возвращаемый после выполнения посланного блоку сообщения value, представляет значение последнего выполненного выражения блока. Блок, который не содержит никаких выражений ([]), когда ему посылается сообщение value, возвращает объект nil, неопределенный объект.
Пример управляющей структуры, реализованной с помощью блоков, - простое повторение действий, представленное посылкой целому числу сообщения timesRepeat: с блоком в качестве аргумента. В ответ целое число посылает блоку-аргументу сообщение value столько раз, сколько единиц содержит значение числа. Следующее выражение четыре раза удвоит значение переменной с именем amount:
4 timesRepeat: [amount <- amount + amount]
Условный выбор похож на оператор if-then-else в алголоподобных языках и обеспечивается сообщением к логическим объектам с именем сообщения ifTrue:ifFalse: (еслиИстина:еслиЛожь:) и двумя аргументами-блоками. Например, следующее выражение присваивает 0 или 1 переменной parity, в зависимости от того, делится на 2 значение переменной number или нет. Бинарное сообщение \\ вычисляет остаток от деления целых чисел.
(number \\ 2) = 0 ifTrue: [parity <- 0] ifFalse: [parity <- 1]Предыдущий пример можно записать и по другому:
parity <- (number \\ 2) = 0 ifTrue: [0] ifFalse: [1]
[:array | total <- total + array size]Типичное использование блоков с аргументами - реализация функций, применяемых ко всем элементам некоторой структуры данных. Например, многие объекты, представляющие собой различные виды структур данных отвечают на сообщение do: (выполнить:), которое имеет аргументом блок с одной переменной. Объект-получатель, получив сообщение do:, выполняет блок-аргумент один раз для каждого своего элемента. Каждый элемент из получателя становится значением аргумента блока в течении одного выполнения блока. Следующий пример вычисляет сумму квадратов первых пяти простых чисел. Результатом будет значением переменной sum.
|sum| sum <- 0. #(2 3 5 7 11) do: [:prime | sum <- sum + (prime * prime)]Объекты, которые реализуют подобные управляющие структуры, поставляют значения аргументам блока, посылая блоку сообщение value: anObject, с аргументом anObject, значение которого присваивается параметру блока. Например, выполнение следующих выражений приведет к тому, что переменная total будет иметь значение 7:
|sizeAdder total| sizeAdder <- [:array | total <- total + array size]. total <- 0. sizeAdder value: #(a b c). sizeAdder value: #(1 2). sizeAdder value: #(e f)Блоки могут иметь и более одного аргумента. Все аргументы блока локальны по отношению к блоку и его выполнению. Например,
[:x :y | (x * x) + (y * y)]Приведенный блок может быть выполнен посредством посылки ему сообщения с именем value:value:. Два аргумента этого сообщения по порядку задают значения двум аргументам блока-получателя. Если блок получает сообщение с количеством ключевых слов value: отличным от числами аргументов блока, то будет выдано сообщение об ошибке.
Можно сказать, что мы завершили наше краткое описание идеологии, структуры и синтаксиса языка, которые не очень то и сложны. Тем не менее, система является чрезвычайно мощной благодаря количеству и разнообразию содержащихся в ней объектов.
В системе Smalltalk-80 восемь важнейших категорий классов: ядро системы и его поддержка, линейные величины, числа, наборы, потоки, классы, независимые процессы и графика (см. приведенную выше часть иерархии классов). Детальные описания протокола каждого системного класса рассматриваются в двенадцати главах второй части книги. Здесь содержится энциклопедическая информация о протоколе класса: определяются категории сообщений, каждое сообщение комментируется, приводятся примеры. В представляемом протоколе класса описываются только те сообщения, которые вводятся самим классом. Полный протокол сообщений определяется просмотром протокола класса и всех его суперклассов. Поэтому полезно начать изучение с класса Object и продолжать его, спускаясь по иерархии классов вниз, с тем, чтобы наследуемый протокол изучался совместно с собственным протоколом класса. Еще в трех главах приводятся примеры использования базовых классов системы для построения небольших приложений.
Но знать - это еще не все, главное - научиться думать о решаемой задаче в терминах объектов, классов, методов, уметь выделить их и "запрограммировать"! Именно этому и посвящена последняя часть книги, описывающая примеры построения компьютерных моделей для дискретных, управляемых событиями процессов.
Моделирование здесь - это представление множества объектов из реального или воображаемого мира в виде объектов некоторых классов. А цель создания компьютерной модели - предоставление рабочей средй для изучения моделируемой ситуации. Прежде чем создавать модели, в книге описывается иерархия классов, представляющих распределения вероятностей. Для определения, например, времени прибытия в модель объектов (клиентов в банк!) применяются различные виды распределения вероятностей. Они же используются и при случайном выборе времени ответа или времени обслуживания клиентов в модели. Класс SimulationObject (ОбъектМодели) представляет любой вид объекта, который вводится в модель для того, чтобы выполнить в ней одну или более задач; класс Simulation (Модель) представляет саму модель и обеспечивает управляющие структуры для ввода и формулировки задач каждому новому объекту модели.
Объекты, которые участвуют в управляемой событиями модели, действуют более или менее независимо друг от друга, используя имеющиеся в модели ресурсы. Поэтому необходимо решать проблему координации и синхронизации их действий. В системе Smalltalk-80 существуют средства синхронизации независимых событий, которые обеспечиваются классами Process (Процесс), Semaphore (Семафор) и SharedQueue (РазделяемаяОчередь). Классы, поддерживающие создание моделей, описывают использование объектами модели расходуемых, не расходуемых и/или возобновляемых ресурсов и предоставляют несколько способов сбора статистической информации о действующих моделях.
Для особенно любопытных, в первой главе третьей части описываются особенности системы с точки зрения разработчика, и объясняется как работает виртуальная машина Smalltalk.
Чтобы продемонстрировать то, о чем рассказывалось выше и отметить еще некоторые особенности, приведем простой пример, взятый из той же книги и слегка измененный нами далеко не лучшим образом только для того, чтобы в одном примере охватить побольше деталей (все равно не все!). Сначала, ничего не объясняя, приведем полное определение нового класса.
class name FinancialHistory superclass Object instance variable names allIncomes incomes allExpenditures expenditures class variable names RateTax class methods initialize initialize RateTax <- 0.12 instance creation initialBalance: amount ^super new setInitialBalance: amount new ^super new setInitialBalance: 0 instance methods transaction recording receive: amount from: source incomes at: source put: (self totalReceivedFrom: source) + amount. allIncomes <- allIncomes + amount spend: amount for: reason expenditures at: reason put: (self totalReceivedFrom: source) + amount. allExpenditures <- allExpenditures - amount inquiries cashOnHand ^allIncomes - allExpenditures amountOfTax ^(RateTax * allIncomes / (1 - RateTax)) truncate totalReceivedFrom: source (incomes includesKey: source) ifTrue: [^incomes at: source] ifFalse: [^0] totalSpentFor: reason (expenditures includesKey: reason) ifTrue: [^expenditures at: reason] ifFalse: [^0] private setInitialBalance: amount allIncomes <- amount. allExpenditures <- 0. incomes <- Dictionary new. expenditures <- Dictionary newОписание класса начинается с заголовка, в котором указывается, что класс с именем FinancialHistory (ФинансовыйОтчет) создается как подкласс класса Object, он имеет одну переменную класса с именем RateTax (СтавкаНалога), доступную для всех его экземпляров, а каждый экземпляр класса имеет четыре переменных экземпляра, определяющих его структуру, с именами allIncomes (всеДоходы), incomes (доходы), allExpenditures (всеРасходы), expenditures (расходы). Первая и третья переменные будут хранить общие суммы всех доходов и расходов, соответственно, вторая и четвертая - словари, содержащие суммы и источники доходов и расходов.
Класс включает в себя часть, называемую class methods (методы класса), в которой описываются методы, добавляемые метаклассом для обращения к классу, как объекту системы. Таким образом, методы класса и методы экземпляра (instance methods) описываются вместе как части полного описания реализации класса.
В каждом классе методы разбиваются на категории, которые объединяют методы, используемые в родственных целях. Методы класса представлены в двух категориях. В первой, initialize, только один метод для инициализации переменной класса, во второй, instance creation, два метода для создания новых экземпляров класса, с разным количеством наличных денег.
Обратите внимание, что методы создания экземпляров (сообщения initialBalance: и new) не имеют прямого доступа к переменным нового экземпляра, поскольку являются не частью класса, которому принадлежат новые экземпляры, а частью класса класса (метакласса). Поэтому методы создания экземпляров сначала создают экземпляры с неинициализированными переменными, а потом посылают им сообщение инициализации setInitialBalance:. Метод для этого сообщения находится в описании реализации класса FinancialHistory уже среди методов экземпляра. Этот метод "умеет" присваивать переменным экземпляра соответствующие значения. Сообщение инициализации не считается частью внешнего протокола класса FinancialHistory, а классифицируется как частное и находятся в категории частных методов (private). Обычно такие сообщения посылаются только другими методами.
Методы экземпляра представлены еще в двух категориях. В категории transaction recording, содержаться методы которое позволяют запомнить откуда и сколько получено доходов (receive: amount from: source), и сколько и по какой причине потрачено (spend: amount for: reason). В категории inquiries, содержаться методы которое позволяют определить сколько получено денег из данного источника (totalReceivedFrom: source), сколько денег потрачено по указанной причине (totalSpentFor: reason), сколько еще осталось денег (cashOnHand) и, наконец, сколько из всего заработанного было уплачено подоходного налога (amountOfTax), предполагая ради простоты, что ставка налога не зависит от суммы и равна 12%.
Используемые в телах методов псевдопеременные self и super всегда ссылается на объект, вызвавший для исполнения этот метод. Между ними есть существенное различие: поиск метода, посланного self всегда начинается с класса, которому принадлежит приемник сообщения, а поиск метода, посланного super начинается с суперкласса того класса, в которм найден метод, содержащий псевдопеременную super. Символ ^ - стрелка вверх, это символ возврата значения, который, если встречается по ходу вычисления, допускает вычисление только выражения стоящего непосредственно после него (до следующей за ним точки!), прекращает выполнение метода и возвращает результат этого выражения, как результат всего метода.
Чтобы использовать класс, надо сначала его инициализировать:
FinancialHistory initialize.После этого можно создать книгу учета доходов и расходов некой семьи, посредством вычисления следующих выражений:
FamilyFinancialHistory <- FinancialHistory new. FamilyFinancialHistory receive: 500000 from: 'зарплатаЖены'; receive: 400000 from: 'зарплатаМужа'; receive: 300000 from: 'пенсияТещи'. FamilyFinancialHistory spend: 150000 for: 'наПродукты'; spend: 50000 for: 'наКниги'; spend: 300000 for: 'наОдежду'.Если теперь вычислим выражение FamilyFinancialHistory cashOnHand, то с удивлением обнаружим, что еще осталось 700,000 руб до следующей получки!
Завершая статью, вспомним фразу еще одного древнего не программиста: "Вполне вероятно наступление невероятного!"(Агафон) и отнесем ее к вероятности выхода Голубой Книги программирования на Русском языке!
| Smalltalk в России | РГУ | Smalltalk в РГУ |
Copyright ©