10.11. OO model

10.11.1. Introduction in OO model

Firstly, some words about CA-Clipper built-in OO model. Its OO is based on ordinary array, and any call like

obj:attribute

or

obj:method()

results in the situation when in obj array there is searching of an element whose first element coincides with attribute or method name, and such search is executed linearly and is practically analogues to

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

function, which greatly reduces the efficiency of OO model of pure CA-Clipper. This, of course, is rather a simplified explanation, but the sense is still that as described.

We believe, now it's clear for what purpose association arrays were made. OO model based on association arrays is faster by an order.

At the same time, there's no need in expressions like

obj:=TClass(class_name):new()

and in TClass class itself, which raises OO model efficiency.

How could you make your own class? It's very simple:

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

Also, we'd like to add two simple rules:

  1. The attribute is created when something, including NIL, is assigned to it.

  2. At any moment in run-time,the method can assign or reassign any function announced in this module as static function, or can adopt this function from another object, as a usual assignment of values:

    myObj1:method1 := myObj2:methodX

In what way can objects be used? As in CA-Clipper, or even more simply:

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

In an object, destroy() method can be announced, but it isn't quite destructor, which is usual in languages of the third generation. There is a local obj variable, and this is just an object. Upon leaving the function body, this variable, along with all its data, is destroyed. Now, let's consider the case of an object having an attribute

obj:hFile:=fopen("somefile")

When destroying obj, it's necessary to close hFile, but the compiler doesn't know this; the compiler (rather a virtual machine) only knows that in hFile there's a number and will destroy the number only, but the file will remain open. Just for such purposes destroy() is intended, and it'll called (if it exists ) before destroying obj variable.

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

10.11.2. Control of change attributes

If it's necessary to control changes of object attributes, make modify() method and invoke

mapmodify(obj, .t. )

modify() method is called before changing value of any object attribute. Two parameters are passed to modify(): hash code of attribute to be changed and new value to be assigned to it. modify() should return the value to assign to the attribute. For example:

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. Recovering/reviving objects

CLIP is able to store the data of any type to MEMO fields, including objects. But there is no way to store object methods (methods can be changed).

Recovering is made with the following steps: data are decoded; if data are of object type and object have CLASSNAME attribute then called function _recover_&(var:CLASSNAME)(var). This function must assign this object methods.

This feature can be used to send object as string via e-mail or TCP :)

Here is an example of using this feature:

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. Overloading operators for objects

CLIP supports overloading of the operations. Operations that can be overloaded and corresponding operator methods are listed in the table below.

Here is an example of using operations overloading.

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. Conclusion

Due to such OO model and compiling into C-program, there appears a possibility to write TBrowse and Get standard classes in Clipper itself. At the same time, the efficiency of these classes is not worse than those written in pure C in CA-Clipper.