Во первых, несколько слов об ОО модели, встроенной в 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
Хотелось бы еще добавить два простых правила:
Атрибут рождается при первом присвоении в него чего-нибудь, в том числе и NIL.
Методом можно назначить или переназначить в любой момент любую функцию, объявленную как 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
Если надо контролировать изменения атрибутов объекта, то сделайте метод 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
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
CLIP поддерживает перегрузку операций для объектов. Перегружаемые операции и соответствующие имена методов перечислены в следующей таблице.
Таблица 10-1. Перегружаемые операции
Операция | Метод | Операция | Метод |
---|---|---|---|
'+' | operator_add | '-' | operator_sub |
'*' | operator_mul | '/' | operator_div |
'%' | operator_mod | '^' | operator_pow |
'|' | operator_or | '&' | operator_and |
'$' | operator_in | '=' | operator_eq |
'==' | operator_eeq | '!=' | operator_neq |
'<' | operator_lt | '>' | operator_gt |
'<=' | operator_le | '>=' | operator_ge |
Вот пример использования перегрузки операций.
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
Благодаря такому устройству ОО-модели и возможности компилировать быстрый код посредством трансляции в С-программу, появилась возможность написать стандартные классы TBrowse, Get на самом CA-Clipper.При этом визуально производительность этих классов не хуже, чем написанные на чистом C в стандартном CA-Clipper.
Пред. | Начало | След. |
Строки как массивы | Уровень выше | Заменяемые Драйвера Баз данных (RDD - Replaceable Database Drivers) |