10.11. ОО модель

10.11.1. Введение в ОО модель

Во первых, несколько слов об ОО модели, встроенной в CA-Clipper. Она основана на использовании обычных массивов и любой вызов, подобный

obj:attribute

или

obj:method()

приводит к тому, что в массиве obj производится поиск элемента, у которого первый элемент совпадает с именем атрибута или метода, причем такой поиск производится линейно и практически является аналогом функции

ascan(obj,{|x|x[1]=="attribute"})

что естественно очень плохо сказывается на производительности ОО-модели CA-Clipper. Это конечно упрощенное описание, но смысл остается такой же.

Теперь должно быть понятно для чего предназначены ассоциативные массивы. ОО модель, основанная на ассоциативных массивах производительнее на порядок.

При этом исчезает необходимость в конструкциях

obj:=TClass(class_name):new()

и в самом классе TClass, что ещеувеличивает производительность.

Как сделать свой класс? Очень просто:

function MyClassNew()
obj:=map() // empty object
clone(MyClass2New(),obj) // adoption of MyClass2 structure
clone(MyClass3New(),obj) // adoption of MyClass3 structure
// if there are coinciding attributes or
// methods, elements of the last class
// become main.
obj:attribute1:=0
obj:attribute2:=date()
obj:method1:=@func1() // method1 becomes a function reference
obj:method2:=@func2()
// these functions must be defined in
// the same .prg file as static
// if methods have been addopted from other classes,
// they will be reassigned to indicated classes
return obj // returning a ready object

static function func1
::attribute1++
return NIL
static function func2(self)
self:attribute1--
return self

Хотелось бы еще добавить два простых правила:

  1. Атрибут рождается при первом присвоении в него чего-нибудь, в том числе и NIL.

  2. Методом можно назначить или переназначить в любой момент любую функцию, объявленную как static в этом же .prg модуле,либо наследовать его от другого объекта, как обычное присвоение значений:

    myObj1:method1 := myObj2:methodX

Как использовать объекты? Так же как и в CA-Clipper:

obj:=MyClassNew()
obj:method1()
? obj:attribute1

В объекте можно объявить метод destroy(), но это не совсем деструктор, как это принято в языках 3 поколения. Есть переменная obj и в ней лежит объект. При выходе из тела функции эта переменная будет уничтожена со всеми вложенными в ней данными. Теперь рассмотрим ситуацию, в котором у этого объекта есть атрибут

obj:hFile:=fopen("somefile")

При уничтожении obj необходимо закрыть hFile, но компилятор (точнее виртуальная CLIP-машина), не знает этого. Он знает только что hFile - это число, и уничтожает только это число, а файл остается открытым. Для подобных случаев и предназначен метод destroy(), который вызывается (если есть) перед уничтожением переменной obj виртуальной CLIP-машиной.

static function my_destroy()
fclose(::hFile)
return

10.11.2. Контроль за изменением атрибутов

Если надо контролировать изменения атрибутов объекта, то сделайте метод modify() и установите

mapmodify(obj, .t. )

Метод modify() вызывается перед изменением значения любого атрибута объекта. Ему передается два параметра: хэш-код изменяемого атрибута и его новое значение. modify() должен вернуть значение, которое затем присвоится атрибуту. Например:

obj := MyObj()
obj:attr1 := "bye"
? obj:attr1,obj:attr2 // hello world

function MyObj()
local obj := map()
obj:attr1 := ""
obj:attr2 := "world"
obj:modify := @mymodify()
mapmodify(obj,.t.)
return obj

static function mymodify(self,hash,value)
if hash == hash_ATTR1 .and. value == "bye"
	return "hello" // don't be so pessimistic :)
endif
return value

10.11.3. Регенерация объектов

CLIP способен хранить данные любых типов в мемо-полях, в том числе и объекты. Но методы объекта не сохраняются (потому что нет смысла хранить код с каждым экземпляром, да и методы имеют тенденцию к изменению).

Восстановление объекта производится следующим образом: данные раскодируются, и если объект имеет атрибут CLASSNAME вызывается функция _recover_&(var:CLASSNAME)(var). Эта функция должна восстановить методы объекта.

Этим же механизмом можно пользоваться и для передачи объектов в виде строки. Например по почте или TCP-соединению :)

Вот пример использования механизма регенерации объектов:

x:=asdfNew() /* Constructor */
? "x:m1",x:m1() /* See wheter it works */
? "x:m2",x:m2()
y:=var2str(x) /* object -> string */
/* or field->memo_field:=x */
? "y=",y
z:=str2var(y) /* string -> object, _recover_asdf() is called automatically */
/* or z:=field->memo_field */
? "z=",z
? "z:m1",z:m1() /* see wheter it works now */
? "z:m2",z:m2()
return

function asdfNew()
local o:=map()
o:classname := "ASDF"
o:a1 := "asdf"
o:a2 := "qwer"
_recover_asdf(o)
return o

function _recover_asdf(o)
o:m1 :=@asdf_1()
o:m2 :=@asdf_2()
? "recovering"
return o

static function asdf_1
? "asdf_1",::a1
return ::a1

static function asdf_2
? "asdf_2",::a2
return ::a1

10.11.4. Перегрузка операций

CLIP поддерживает перегрузку операций для объектов. Перегружаемые операции и соответствующие имена методов перечислены в следующей таблице.

Вот пример использования перегрузки операций.

car1 := newBMW()
car2 := newKAMAZ()
? car1 > car2 // .F. (weight of Kamaz is much more :)
car := car1+car2
? car:model // Scrap
? car:weight // 10000
// 10000 kilograms of the scarp-iron :)

function newCar()
local obj := map()
obj:model := ""
obj:weight := 0
obj:operator_gt := @car_gt()
obj:operator_add := @car_add()
return obj

static function car_gt(car)
return ::weight > car:weight

static function car_add(car)
local obj := newCar()
obj:model := "Scrap"
obj:weight := ::weight+car:weight
return obj

function newBMW()
local obj := newCar() // adopt Car class
obj:model := "BMW"
obj:weight := 2000
return obj

function newKAMAZ()
local obj := newCar() // adopt Car class
obj:model := "KAMAZ"
obj:weight := 8000
return obj

10.11.5. Заключение

Благодаря такому устройству ОО-модели и возможности компилировать быстрый код посредством трансляции в С-программу, появилась возможность написать стандартные классы TBrowse, Get на самом CA-Clipper.При этом визуально производительность этих классов не хуже, чем написанные на чистом C в стандартном CA-Clipper.