Morphic is the parent class of ProxyService and delegates its identity to its defaultHandler
class. A Morphic class object can thus change its state and behavior
at runtime by changing the defaultHandler. But
Proteus makes use of this class through its ProxyService
class.
The Morphic class inherits from PreinitObject, and directly defines only those properties necessary to concatenate
its ‘self’ identity with that of its defaultHandler, a construct() method to
initialize its vocabulary and containment relationships, a defaultHandler property, and a propNotDefined()
method to capture all other messages and delegate them to its defaultHandler object.
In addition, the proteus.h
header defines templates for Morphic similar to those
provided for Thing, to facilitate shortcut vocabulary, description, and
location properties.
Suppose we want to make a
bowl of class Container. We could use the usual definition:
bowl: Container
{
‘bowl’ ‘bowl’ @livingRoom
}
This object inherits its
states and behaviors from Container class. We can examine the definition returned
by the @show metacommand:
Object staticBowl extends Container
{
DynamOrAnon: nil UnnamedInternal: nil
Transient: nil
properties directly defined by object
staticBowl
vocabWords_ : TypeSString =
'static bowl'
location : TypeObject = livingRoom
name : TypeSString = 'static bowl'
adjective : TypeList = ['static']
noun : TypeList = ['bowl']
objRefTag : TypeSString =
'tads#55c'
tmpAmbient_ : TypeInt = 0
tmpAmbientWithin_ : TypeInt = 0
tmpAmbientFill_ : TypeNil = nil
tmpTrans_ : TypeEnum = transparent
tmpTransWithin_ : TypeEnum =
opaque
tmpObstructor_ : TypeNil = nil
tmpObstructorWithin_ : TypeNil =
nil
tmpFillMedium_ : TypeNil = nil
explicitVisualSenseInfo : TypeNil
= nil
}
Alternatively, we could
define our bowl using the Morphic class, as follows:
bowl: Morphic
{
'bowl' 'bowl' @livingRoom
defaultHandler = Container
}
Let’s examine the Morphic bowl using the @show command:
Object morphicBowl extends Morphic,
tads#64f (Container)
{
DynamOrAnon: nil UnnamedInternal: nil
Transient: nil
properties directly defined by object
morphicBowl
vocabWords_ : TypeSString =
'morphic bowl'
location : TypeObject = livingRoom
name : TypeSString = 'morphic
bowl'
defaultHandler : TypeObject =
tads#64f (Container)
isDoingExec_ : TypeNil = nil
isExecuted_ : TypeTrue = true
objRefTag : TypeSString =
'tads#577'
adjective : TypeList = ['morphic']
noun : TypeList = ['bowl']
tmpAmbient_ : TypeInt = 0
tmpAmbientWithin_ : TypeInt = 0
tmpAmbientFill_ : TypeNil = nil
tmpTrans_ : TypeEnum = transparent
tmpTransWithin_ : TypeEnum =
opaque
tmpObstructor_ : TypeNil = nil
tmpObstructorWithin_ : TypeNil =
nil
tmpFillMedium_ : TypeNil = nil
explicitVisualSenseInfo : TypeNil
= nil
}
From this we can see that the
Morphic bowl defines a few extra properties: &isDoingExec_ and &isExecuted_.
These belong to the PreinitObject class and were generated when the object was
constructed.
But notice that the Morphic bowl extends an instance
of Container, rather than Container
class itself. The reason for this is to perform a subtle piece of trickery with
the propDefined() method. Consider the following:
propDefined(&isOpen) |
morphicBowl |
staticBowl |
PropDefAny |
true |
true |
PropDefDirectly |
nil |
nil |
PropDefInherits |
true |
true |
PropDefGetClass |
Container |
Container |
By delegating to an instance of the handler class, instead
of the class itself we maintain the same relationships of the propDefined() intrinsic method for morphicBowl as for staticBowl. It also allows getPropList()
and getPropParams() to remain consistent with that of staticBowl.
The states and behaviors of
the object are roughly the same as though it inherited from Container, but it still has some differences, due to the
structures of their inheritance trees.
But the biggest difference is
that the Morphic object can change its class structure during runtime.
For instance, suppose we want the bowl to change from Container to Surface class when
something is put into it. We can code this as
follows:
morphicBowl:
Morphic
{
'morphic bowl' 'morphic bowl' @livingRoom
defaultHandler = Container
iobjFor(PutIn)
{
action()
{
delegated (getHandler())();
setHandler(Surface);
}
}
}
Notice that we can’t use the inherited() keyword to pass control down the inheritance tree,
we must use the delegated() keyword (the same command that is used by propNotDefined()). Now when an object is put into the bowl it is
converted into a Surface. Examining the object definition with the @show metacommand gives us the following:
Object morphicBowl extends Morphic,
tads#6bd (Surface)
{
DynamOrAnon: nil UnnamedInternal: nil
Transient: nil
properties directly defined by object
morphicBowl
vocabWords_ : TypeSString =
'morphic bowl'
location : TypeObject = livingRoom
name : TypeSString = 'morphic
bowl'
actionIobjPutIn(0, 0, nil) :
TypeCode
defaultHandler : TypeObject =
tads#6bd (Surface)
isDoingExec_ : TypeNil = nil
isExecuted_ : TypeTrue = true
objRefTag : TypeSString =
'tads#577'
adjective : TypeList = ['morphic']
noun : TypeList = ['bowl']
tmpAmbient_ : TypeInt = 0
tmpAmbientWithin_ : TypeInt = 0
tmpAmbientFill_ : TypeNil = nil
tmpTrans_ : TypeEnum = transparent
tmpTransWithin_ : TypeEnum =
tmpObstructor_ : TypeNil =
tmpObstructorWithin_ : TypeNil =
tmpFillMedium_ : TypeNil =
explicitVisualSenseInfo : TypeNil
=
}
>x morphic
Of course we would probably
want to change the vocabulary and name of the bowl to represent its new
structure.
This file is part of the TADS 3
Proteus Library Extension
Copyright ©
2001-2004 Kevin Forchione.