<< Назад

5.4. Другие объектно-ориентированные системы программирования

Язык C++ является сейчас наиболее распространенным объектно-ориентированным языком. Однако существуют и другие популярные объектно-ориентированные языки и системы программирования. Здесь мы кратко рассмотрим языки Eiffel и Smalltalk.

5.4.1. Реализация классов

Определение класса WINDOW на языке EIFFEL мало отличается от соответствующего определения на языке C++ (сравните с п. 5.3.1):



	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 ofWidth: 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

5.4.2. Порождение объектов

В языке 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.

	     |w

	instance methods

	     initialize: aPoint ofWidth: width ofHeigt: heigt.

	     xmin <- aPoint x.

	     ymin <- aPoint y.

	     xmax <- xmin + width.

	     ymax <- ymin + height

Отметим, что метод уровня класса не имеет непосредственного доступа к атрибутам объектов. Поэтому для инициализации окна потребовалось определить метод уровня объекта initialize.

5.4.3. Вызов операций

В языке 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, именующая целевой объект (получатель сообщения).

5.4.4. Реализация наследования

В языке 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

Все атрибуты суперкласса доступны всем его потомкам. Все методы могут быть переопределены в подклассах. Множественное наследование не поддерживается.

5.4.5. Реализация зависимостей

В языке 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 не поддерживает приватности методов и данных, и на самом деле все методы общедоступны.

 

<< Назад | Содержание | Вперед >>