Другие объектно-ориентированные системы программирования
Язык C++ является сейчас наиболее распространённым объектно-ориентированным языком. Однако существуют и другие популярные объектно-ориентированные языки и системы программирования. Здесь мы кратко рассмотрим языки Eiffel и Smalltalk.
Реализация классов
Определение класса Window на языке Eiffel мало отличается от соответствующего определения на языке C++:
class WINDOW
export
add_box, add_circle, clear_selections, cut_selections,
group_selections, move_selections,
redraw_all, select_item, ungroup_selections
feature
xmin, ymin, xmax, ymax: REAL;
Create (x0, y0, width, height: REAL) is body end;
add_box (x, y, width, height: REAL) is body end;
add_circle (x, y, radius: REAL) is body end;
add_to_selections (ashape: SHAPE) is body end;
clear_selections is body end;
cut_selections is body end;
group_selections: Group is body end;
move_selections (deltax, deltay: REAL) is body end;
redraw_all is body end;
select_item (x, y: REAL) is body end;
ungroup_selections is body end;
end -- class WINDOW
Все свойства (feature) класса (соответствуют членам класса в языке C++) считаются приватными, если только они не объявлены явно как общедоступные: все общедоступные свойства перечисляются в разделе export. Свойство Create (аналог конструктора класса в языке C++) всегда является общедоступным. Остальные особенности синтаксиса понятны и без пояснений.
Язык Smalltalk является языком интерпретируемого типа: программа в системе Smalltalk не компилируется, а переводится в интерпретируемое специальным интерпретатором внутреннее представление. Это позволило сделать систему Smalltalk интерактивной: программист имеет доступ к программе (может, например, изменять её) во время её интерпретации, что очень удобно при разработке и отладке программы, но, как известно, уменьшает её эффективность, так как интерпретация программы сопряжена с дополнительными накладными расходами.
Определение класса Window на языке Smalltalk имеет следующий вид:
class name Window
superclass Object
instance variables xmin, ymin, xmax, ymax: REAL
class methods
instantiating
createAt aPoint of Width: width ofHeigt: heigt
instance methods
adding shapes
addboxAt aPoint ofWidth: width ofHeigt: heigt
addCircleAt aPoint ofRadius: radius
refreshing window
redrawAll
manipulating selections
clearSelections
cutSelections
groupSelections
moveSelectionsBy: deltaPoint
selectItemAt: aPoint
ungroupSelections
private
addToSelections: aShape
Все классы языка Smalltalk являются подклассами системного класса Object, что позволяет использовать принадлежащие им по наследованию системные операции системы Smalltalk, определённые в классе Object. Строки в описании класса, выделенные курсивом, определяют категории методов. Они служат только для структурирования набора методов. Методы, включённые в категорию private, являются приватными и не доступны извне класса. Чтобы сделать атрибуты приватными, следует не определять методы для запроса и установки значений атрибутов. В системе Smalltalk определён системный метод @, который объединяет пару числовых значений в одно составное значение. Это позволило вместо двух переменных x и y (координат точки) использовать в нашем примере одну переменную aPoint, представляющую обе координаты точки (x,y). Операция присваивания значения (3,4) переменной aPoint с использованием метода @ имеет вид:
aPoint <- 3 @ 4
Порождение объектов
В языке Eiffel объявление переменной (в этом языке вместо термина «переменная» используется термин сущность (entity)) отделено от порождения соответствующего объекта. Объявление сущности определяет имя, значением которого может быть (объектная) ссылка на объект объявленного типа, но эта ссылка при объявлении получает неопределённое значение (void), т.е. не ссылается ни на какой объект. Пример объявления переменной:
w: WINDOW
В результате этого объявления создаётся переменная w, значением которой может быть ссылка на окно, но которая пока не ссылается ни на какое окно. Все объекты языка Eiffel – динамические: они создаются с помощью операции Create; например, следующий оператор создаёт окно с соответствующими параметрами и присваивает ссылку на него переменной w:
w.Create (0.0, 0.0, 8.5, 11.0)
В каждом классе неявно определена операция Create без параметров, которая создаёт новый объект этого класса с нулевыми значениями его атрибутов. Можно переопределить неявную операцию Create; например, для окон эту операцию можно определить следующим образом:
class WINDOW
...
feature
Create (x0, y0, width, height: REAL) is
do
xmin := x0; ymin := y0;
xmax := x0 + width; ymax := y0 + height;
end
...
end -- class WINDOW
Кроме операции Create, определена операция создания копий уже существующего объекта – Clone.
В языке Eiffel невозможно явным образом уничтожить объект (в нём отсутствует операция, подобная операции delete языка C++). Операция Forget убирает объектную ссылку из соответствующей сущности, но не уничтожает сам объект. Объект, на который нет ни одной объектной ссылки, уничтожается во время «чистки мусора», которая, если она не отключена программистом (для этого имеется специальная системная операция), выполняется автоматически.
Все объекты языка Smalltalk – динамические и размещаются в динамической памяти – куче. Удаление объектов осуществляется автоматически подсистемой чистки мусора. Все переменные не имеют типа и могут содержать объекты любого класса. Порождение объектов осуществляется операцией new, определённой в системном классе Object (все классы языка Smalltalk – наследники класса Object). Например, порождение окна со стандартными параметрами (определяемыми по умолчанию) осуществляется операцией:
w <- Window new
Операция new является одним из методов уровня класса. С её помощью можно определить ещё один метод порождения окна (уже с параметрами):
w <- Window createAt: 0 @ 0 ofWidth: 8.5 ofHeight: 11.0
Этот метод может быть определён следующим образом:
class name Window
...
class methods
createAt: aPoint ofWidth: width ofHeigt: heigt
| w |
w <- self new.
w initialize: aPoint ofWidth: width ofHeigt: heigt.
instance methods
initialize: aPoint ofWidth: width ofHeigt: heigt.
xmin <- aPoint x.
ymin <- aPoint y.
xmax <- xmin + width.
ymax <- ymin + height
Отметим, что метод уровня класса не имеет непосредственного доступа к атрибутам объектов. Поэтому для инициализации окна потребовалось определить метод уровня объекта initialize
Вызов операций
В языке Eiffel методы называются подпрограммами (routines). При вызове этих подпрограмм им передаются параметры, которые могут иметь простой тип (REAL, INTEGER, BOOLEAN, или CHARACTER) или быть объектами классов, определенных программистом. В подпрограмме не разрешается изменять значения формальных параметров путём присваивания им новых значений или применения к ним операции (такие как Create, Clone или Forget), которые могут менять значение объектной ссылки. В то же время остальные операции могут быть применены к объектам, являющимся формальными параметрами, что вызовет изменение состояния указанных объектов.
Синтаксис вызова операции в языке Eiffel такой же, как в языке C++, причём операция '.' языка Eiffel соответствует операции '->' языка C++:
local
aShape: SHAPE;
dx, dy: REAL
do
...
aShape.move (dx, dy);
end
В языке Eiffel определён неявный доступ к свойствам целевого объекта по имени соответствующего свойства. Идентификаторы x и y являются именами атрибутов целевого объекта SHAPE:
move (deltax, deltay: REAL) is
-- move a shape by delta
do
x = x + deltax;
y = y + deltay
end
В языке Eiffel имеется предопределённый идентификатор Current, который именует целевой объект операции (он аналогичен идентификаторам this языка C++ и self языка Smalltalk). Поэтому предыдущий фрагмент программы можно записать следующим эквивалентным образом:
move (deltax, deltay: REAL) is
-- move a shape by delta
do
Current.x = Current.x + deltax;
Current.y = Current.y + deltay
end
В языке Smalltalk все параметры и переменные являются объектами; все операции являются методами, связанными с этими объектами. Применить операцию к объекту – значит послать этому объекту сообщение, состоящее из имени операции и списка значений её параметров. Сообщение связывается с объектом во время выполнения программы (динамически), рассматривается класс соответствующего объекта и находится соответствующая операция в этом классе или в его предках (по наследованию). Формальные параметры метода запрещено изменять внутри метода с помощью присваиваний. Синтаксис обращения к операции (посылки сообщения) следующий:
aShape moveDelta: aPoint
Этот метод можно реализовать следующим образом:
class name Shape
instance variables
x y
instance methods
moveDelta: aPoint
x <- x + aPoint x
y <- y + aPoint y
Внутри метода разрешён непосредственный доступ по имени к атрибутам целевого объекта операции (в языке Smalltalk атрибуты называются переменными объекта (instance variables)).
Как уже упоминалось, в языке Smalltalk предопределена псевдо-переменная self, именующая целевой объект (получатель сообщения).
Реализация наследования
В языке Eiffel список наследования помещается вслед за ключевым словом inherit:
class ITEM
export
cut, move, pick, ungroup
feature
cut is deferred end;
move (deltax, deltay: REAL) is deferred end;
pick (x, y: REAL): BOOLEAN is deferred end;
ungroup () is deferred end
class SHAPE
export
cut, draw, erase, move, pick, ungroup, write
inherit ITEM
feature
* x, y: REAL;
cut is <body> end;
draw is <body> end;
erase is <body> end;
move (deltax, deltay: REAL) is <body> end;
pick (x, y: REAL): BOOLEAN is <body> end;
ungroup is <body> end;
write (acolor: COLOR) is deferred end;
end
classrBOX
export pick, write
inherit SHAPE redefine pick, write
feature
width, height: REAL;
Create (x0, y0, width0, height0:*REAL) is <body> end;
pick (x, y: REAL): BOOLEAN is <body> end;
write (acolor: COLOR) is <body> end
end
class CIRCLE
export pick, write
inherit SHAPE redefine pick, write
feature
radius: REAL;
Create (x0, y0, radius0: REAL) is <body> end;
pick (x, y: REAL): BOOLEAN is <body> end;
write (acolor: COLOR) is <body> end
end
Для обозначения абстрактных операций используется ключевое слово deferred; такие операции должны быть реализованы во всех подклассах. Переопределение свойств класса в подклассе отмечается в разделе redefine.
В языке Smalltalk описание класса Item, его подкласса Shape, а также подклассов Box и Circle класса Shape может иметь следующий вид:
class name Item
superclass Object
class name Shape
superclass Item
instance variables
x
y
instance methods
cut
draw
erase
move: aPoint
ungroup
class name Box
superclass Shape
instance variables
width
height
instance methods
pick: aPoint
write: aColor
class methods
createAt: aPoint width: widthSize length: lengthSize
class name Circle
superclass Shape
instance variables
radius
instance methods
pick: aPoint
write: aColor
class methods
createAt: aPoint radius: radiusSize
Все атрибуты суперкласса доступны всем его потомкам. Все методы могут быть переопределены в подклассах. Множественное наследование не поддерживается.
Реализация зависимостей
В языке Eiffel для реализации зависимостей применяются конструкции, аналогичные соответствующим конструкциям языка C++. Поддерживаются параметризованные (родовые (generic)) контейнерные объекты (обычно эти объекты параметризуются относительно типов объектов, которые они содержат). Базовая библиотека классов системы Eiffel содержит контейнерный класс LINKED_LIST, который можно использовать для реализации зависимости типа «много к одному» между классами ITEM и GROUP:
class ITEM
export
get_group
-- выборочный экспорт в класс GROUP
set_group(GROUP), forget_group(GROUP)
feature
get_group: GROUP is
do
Result := mygroup
end;
set_group(g:GROUP) is
do
mygroup := g
end;
forget_group is
do
forget(mygroup)
end;
end --ITEM
class GROUP
export
add_item, remove_item, get_items
inherit ITEM
feature
items: LINKED_LIST[ITEM]; --параметризованный тип
Create is
do
items.Create
end;
add_item(value:ITEM) is
do
items.finish;
items.insert_right(value);
value.set_group(Current)
end;
remove_item(value:ITEM) is
do
items.search(value,l);
items.delete;
value.forget_group
end;
get_items(number:INTEGER):ITEM is
do
Result := items
end;
end --GROUP
Язык Eiffel обеспечивает выборочный экспорт относительно любого свойства. В рассматриваемом примере в класс GROUP экспортируются свойства set_group и forget_group класса ITEM. Это поддерживает инкапсуляцию путём ограничения доступа по записи данных в объекты классов, участвующих в зависимости между ITEM и GROUP.
Операция forget языка Eiffel предназначена для освобождения памяти; она освобождает память, занимаемую объектом, который является её операндом, а также присваивает объектной ссылке неопределённое значение.
В языке Smalltalk большую часть зависимостей помогает реализовать богатая системная библиотека классов. Например, для реализации зависимости типа «много к одному» между графическими объектами (Item) и группой, в которую они входят (Group), можно использовать библиотечный класс Set:
class name Item
superclass Object
class name Shape
superclass Item
instance variables
group
instance methods
cut
draw
erase
move: aPoint
ungroup
--дополнительные методы
getGroup
group
--приватные методы
putGroup: aGroup
group <- aGroup
class name Group
superclass Item
instance variables
items
class methods
new
|((super new)putItems:(Set new))
instance methods
pick: aPoint
write: aColor
addItem: anItem
items add: anItem.
anItem putGroup: self
removeItem
items remove: anItem.
anItem putGroup: nil
getItems
|items copy
--приватные методы
putItems: aSet
items <- aSet
Ввиду того, что в системе Smalltalk не производится контроль типов, нет никаких ограничений на тип объектов, которые можно добавить в группу: любой объект, который соответствует сообщению putGroup:, допустим. Строка приватные методы является комментарием, сообщающим программисту, что методы, перечисленные после этой строки, лучше использовать как приватные; если он не последует этому совету, система всё равно не зафиксирует ошибку, так как Smalltalk не поддерживает приватности методов и данных, и на самом деле все методы общедоступны.