Extended Object

Some pecu­liar­i­ties of the inher­i­tance in Lin­go have always seemed to me to be lim­it­ing fac­tors. By the stan­dard means we are not able to know (for sure) if a giv­en object imple­ments a spe­cif­ic han­dler. Also, we do not know (for sure) if a giv­en object has a spe­cif­ic prop­er­ty. We can not find out what is the object type, nor we can inher­it in an easy and flex­i­ble way a num­ber of parents.
Let me show you the fol­low­ing exam­ple with two scripts — “Bul­let” and “Fly­in­gOb­ject”. The script called “Bul­let” inher­its “Fly­in­gOb­ject”.

—————————————–
— par­ent script “Bul­let”
—————————————–
prop­er­ty ancestor
prop­er­ty type
prop­er­ty damage

on new( me, t )
ances­tor = script( “Fly­in­gOb­ject” ).rawNew()
callAnces­tor( #new, me )
type = t
case( t ) of
5.45: dam­age = 2.7
7.62: dam­age = 3.1
end case
return me
end new

on shoot( me )
nothing
end shoot

on explode( me )
nothing
end explode

—————————————–
— par­ent script “Fly­in­gOb­ject”
—————————————–
prop­er­ty speed

on new( me, s )
speed = s
return me
end new

on fly( me )
nothing
end fly

on col­lide( me )
nothing
end collide 

If we exe­cute the fol­low­ing lines in the Mes­sage window,

myBul­let = script(“Bullet”).new()
trace( myBullet.handlers() ) — [#new, #shoot, #explode]

we shall see that han­dlers() func­tion “sees” only the han­dlers which are defined in the “Bul­let” script. If we decide to “reel” all the prop­er­ties of myBul­let and to check their val­ues, we shall dis­cov­er that we can see only the prop­er­ties defined in the “Bul­let” script.

on show­Prop­er­ties()
myBul­let = script(“Bullet”).new( 5.45 )
repeat with i = 1 to count( myBullet )
p = myBullet.getPropAt( i )
v = myBullet.getAprop( p )
put p & “: ” & v
end repeat
end showProperties

– “ances­tor: ” <off­spring “Fly­in­gOb­ject” 3 85c751c>
— “dam­age: ” 2.7000
— “type: ” 5.4500

Not the last, in Lin­go we can not find out the exact type of a giv­en cus­tom object. The ilk() func­tion always returns #instance.

put ilk( script( “Bul­let” ).new() ) — #instance
put ilk( script( “Fly­in­gOb­ject” ).new() ) — #instance

The “Extende­dOb­ject” script tries to solve some of the above men­tioned prob­lems. From the moment when a giv­en script inher­its “Extende­dOb­ject”, we can use the fol­low­ing new or mod­i­fied* functions:

-- extends()
-- implements()
-- typeOf()
-- is()
-- properties()
-- hasProperty()
-- getHandlers()
-- hasHandler()

So, how to take an advan­tage of the “Extende­dOb­ject” script? Sim­ply every one of the scripts, which we plan to be inher­it­ed as a part of the “mul­ti­ple inher­i­tance” or the scripts, which we like to have the above described func­tion­al­i­ty must inher­it the “Extende­dOb­ject” script. Let us look back at the pre­vi­ous exam­ple. In the con­struc­tor of the “Bul­let” script we delete the lines:

ances­tor = script( “Fly­in­gOb­ject” ).rawNew()
callAnces­tor( #new, me ) 

and on their place we write:

ances­tor = script(“ExtendedObject”).rawNew()
callAnces­tor( #new, me, me )
me.extends( script(“FlyingObject”).new( 1000, 3.5 ) )

In the “Fly­in­gOb­ject” con­struc­tor we add the fol­low­ing lines:

on new( me, s )
ances­tor = script(“ExtendedObject”).rawNew()
callAnces­tor( #new, me, me )
speed = s
return me
end new

Now the things look different:

myBul­let = script(“Bullet”).new( 7.62 )
trace( myBullet.getHandlers() ) — [#new, #shoot, #explode, #dis­pose, #type­Of, #imple­ments, #is, #getH­andlers, #prop­er­ties, #hasProp­er­ty, #hasHan­dler, #extends, #fly, #col­lide]
trace( myBullet.hasHandler( #fly ) ) –TRUE
trace( myBullet.properties() ) — [#ances­tor, #type, #dam­age, #suc­ces­sor, #mass, #speed]
trace( myBullet.hasProperty( #mass ) ) –TRUE
trace( myBullet.typeOf() ) — #Bul­let
trace( myBullet.is( #bul­let ) ) — TRUE
trace( myBullet.is( #Fly­in­gOb­ject ) ) — TRUE
trace( myBullet.is( #Visu­alItem ) ) — FALSE
trace( myBullet.implements() )– [#Bul­let, #Extende­dOb­ject, #Fly­in­gOb­ject]

The new extends() func­tion makes it pos­si­ble for us to make “mul­ti­ple inher­i­tance”. Let me intro­duce anoth­er script in this exam­ple — “Visu­alItem”. We add a line to the “Bul­let” script constructor:

ances­tor = script(“ExtendedObject”).rawNew()
callAnces­tor( #new, me, me )
me.extends( script(“FlyingObject”).new( 1000, 3.5 ) )
me.extends( script( “Visu­alItem” ).new( 5, 10, color(255,0,0) ) )

As “Visu­alItem” will be a part of the “mul­ti­ple inher­i­tance”, it also inher­its “Extende­dOb­ject”. We add the fol­low­ing lines to the “Visu­alItem” constructor:

—————————————–
— par­ent script “Visu­alItem”
—————————————–
prop­er­ty ancestor
prop­er­ty width
prop­er­ty height
prop­er­ty color

on new( me, w, h, c )
ances­tor = script( “Extende­dOb­ject” ).rawNew()
callAnces­tor( #new, me, me )
width = w
height = h
col­or = c
return me
end new

on ren­der( me )
nothing
end render

We can try again in the Mes­sage window:

myBul­let = script(“Bullet”).new( 7.62 )
trace( myBullet.getHandlers() ) — [#new, #shoot, #explode, #dis­pose, #type­Of, #imple­ments, #is, #getH­andlers, #prop­er­ties, #hasProp­er­ty, #hasHan­dler, #extends, #fly, #col­lide, #ren­der]
trace( myBullet.hasHandler( #fly ) ) –TRUE
trace( myBullet.hasHandler( #ren­der ) ) –TRUE
trace( myBullet.properties() ) — [#ances­tor, #type, #dam­age, #suc­ces­sor, #mass, #speed, #width, #height, #col­or]
trace( myBullet.hasProperty( #mass ) ) –TRUE
trace( myBullet.typeOf() ) — #Bul­let
trace( myBullet.is( #bul­let ) ) — TRUE
trace( myBullet.is( #Fly­in­gOb­ject ) ) — TRUE
trace( myBullet.is( #Visu­alItem ) ) — TRUE
trace( myBullet.implements() ) — [#Bul­let, #Extende­dOb­ject, #Fly­in­gOb­ject, #Visu­alItem]

This is how the final result should look in the Object Inspector:

Final­ly, please, pay atten­tion that there is a pos­si­bil­i­ty a two-way com­mu­ni­ca­tion to be cre­at­ed between the child and the ances­tor. If you need this func­tion­al­i­ty, you have to pass the “me” para­me­ter when you call the con­struc­tor of the ances­tor script.

ances­tor = script( “Extende­dOb­ject” ).rawNew()
callAnces­tor( #new, me, me )

When we end work­ing with the myBul­let object, we call the dis­pose() method, which destroys the whole “chain of ancestors”.
There is the script itself:

Show code

– Extende­dOb­ject par­ent script
— Author: Vladin M. Mitov
— August, 2011
— http://www.ed-multimedia.com

– Serves to be inher­it­ed by every script, which is a part of the “chain of ancestors”.
— Adds the fol­low­ing functionality:

– type­Of() — Returns the “type” of the object as a symbol.
— imple­ments() — Returns a list of the types of all the objec­t’s ancestors.
— is() — Checks if the object inher­its a giv­en script;
— Exam­ple: put myObj.is( #Visu­alItem )
— getH­andlers() — Returns a list of all the pub­lic meth­ods of all the ances­tors down the chain.
— prop­er­ties() — Returns a list of all prop­er­ties of all ances­tors down the chain.
— hasProp­er­ty() — Checks if the object has a such a property.
— hasHan­dler() — Checks if the object has such a handler.
— extends() — Adds an ances­tor to the chain.

—————————————————————
PROPERTIES
—————————————————————
prop­er­ty ancestor
prop­er­ty successor

—————————————————————
CONSTRUCTOR / DESTRUCTOR
—————————————————————
on new( me, s )
suc­ces­sor = s
return me
end new

on dis­pose( me )
if( ilk ( ances­tor ) = #instance or \
ilk ( ances­tor ) = #script ) then callAnces­tor( #dis­pose, [ me ] )
suc­ces­sor = void
end dispose

—————————————————————
PUBLIC METHODS
—————————————————————
on type­Of( me )
return me._getType( me )
end typeOf

on imple­ments( me, asorted )
ret­val = list( #Extende­dOb­ject )
tmp = max( suc­ces­sor, me )
repeat while not voidP( tmp )
if( ilk( tmp.getAprop( #ances­tor ) ) = #instance ) then
if( not( tmp.ancestor.handlers().getPos( #type­Of ) = 0 ) ) then
typ = tmp.typeOf()
end if
else
typ = me._getType( tmp )
end if
if( retval.getPos( typ ) = 0 ) then
retval.append( typ )
end if
tmp = tmp.getAProp( #ances­tor )
end repeat
if ( asort­ed = TRUE ) then
retval.sort()
end if
return retval
end implements

on is( me, thetype )
myTypes = me.implements()
return ( myTypes.getPos( thetype ) <> 0 )
end is

on getH­andlers( me, asorted )
ret­val = list()
tmp = max( suc­ces­sor, me )
repeat while not voidP( tmp )
hndlrs = tmp.handlers()
repeat with i = 1 to count( hndlrs )
hand = hndlrs[ i ]
if( string( hand ) starts “_” ) then next repeat
if( retval.getPos( hand ) = 0 ) then
retval.append( hand )
end if
end repeat
tmp = tmp.getAProp( #ances­tor )
end repeat
if ( asort­ed = TRUE ) then
retval.sort()
end if
return retval
end getHandlers

on prop­er­ties( me, asorted )
tmp = max( suc­ces­sor, me )
ret­val = list()
repeat while not voidP( tmp )
repeat with i = 1 to count( tmp )
prop­name = tmp.getPropAt( i )
if( retval.getPos( prop­name ) = 0 ) then
retval.add( propname )
end if
end repeat
tmp = tmp.getAProp( #ances­tor )
end repeat
if ( asort­ed = TRUE ) then
retval.sort()
end if
return retval
end properties

on hasProp­er­ty( me, propname )
tmp = max( suc­ces­sor, me )
repeat while not voidP( tmp )
repeat with i = 1 to count( tmp )
if( tmp.getPropAt( i ) = prop­name ) then
return TRUE
end if
end repeat
tmp = tmp.getAProp( #ances­tor )
end repeat
return FALSE
end hasProperty

on hasHan­dler( me, handlername )
tmp = max( suc­ces­sor, me )
repeat while not voidP( tmp )
hndlrs = tmp.handlers()
repeat with i = 1 to count( hndlrs )
if( hndlrs[ i ] = han­dler­name ) then
return TRUE
end if
end repeat
tmp = tmp.getAProp( #ances­tor )
end repeat
return FALSE
end hasHandler

on extends( me, obj, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z )
if voidP( ances­tor ) then
case( ilk( obj ) ) of
#script:
ances­tor = obj.rawNew()
callAnces­tor( #new, [me], a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z )
otherwise:
ances­tor = obj
end case
else
case( ilk( obj ) ) of
#instance, #script:
callAnces­tor( #extends, [me], obj, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z )
otherwise:
nothing
end case
end if
end extends

—————————————————————
PRIVATE METHODS
—————————————————————
on _getType( me, obj )
the itemde­lim­iter = quote
if( ilk( obj ) <> #instance ) then return #unde­fined
return sym­bol( string( obj ).item[ 2 ] )
end _getType

 

Extend­ed Object (4722 downloads) 
 
Comments

No comments yet.