OO Programming (kinda like BDSM...)
OO Programming (kinda like BDSM...)
- Subject: OO Programming (kinda like BDSM...)
- From: email@hidden
- Date: Mon, 31 Dec 2001 15:55:50 -0500
On Sun, 30 Dec 2001 15:49:45 +0000, has <email@hidden> asked,
fun...)
>
OK, this problem's for object-oriented heads only; I won't be held
>
responsible if anyone else hurts themselves on it.
Its not an object-oriented thing. The same sort of problems occur in any block
structured language. ALGOL surfaced these issues back in 1960.
>
>
I imagine this is a scope issue, but don't understand why.
>
It certainly is. Specifically, its the difference between compile-time and
run-time, or static and dynamic scope. AppleScript assigns meanings to variable
names at compile time, and that keeps you from wrapping a new context around a
variable at run time. I think that's generally a good thing, since it keeps you
from changing the meaning of a script object by placing it in a different
context, but it requires you to think differently about how you craft the
interface between a script object and the outside world.
>
This works:
>
>
======================================================================
>
>
script x
>
script y
>
on foo()
>
beep 2
>
end foo
>
end script
>
>
script z --(z is inside x)
>
y's foo()
-- At this point, the AppleScript compiler recognizes "y"
-- as the "script y" above.
>
end script
>
end script
>
>
tell x's z to run
>
>
======================================================================
>
>
>
This doesn't:
>
>
======================================================================
>
>
script x
>
script y
>
on foo()
>
beep 2
>
end foo
>
end script
>
property z : {}
>
end script
>
>
script z --(z is outside x)
>
y's foo()
-- At compile time, "y" has no special meaning, so "y" is
-- considered just a variable, to be resolved at run time.
-- That means "y" means "y of <<script>>"
>
end script
>
>
set x's z to z --(chuck z into x at runtime)
>
>
tell x's z to run
Here are two examples that shows that the scope of variables is determined by
where the variable was defined at compile time, not where it is at run time.
-- Example 1
property p : "Top of script"
script x
property p : "Script x"
property z : {}
end script
script z
return p
end script
set x's z to z
tell x's z to run
-- Result: "Top of script"
-- Example 2
property p : "Top of script"
script x
property p : "Script x"
property z : {}
script zOriginal
return p
end script
end script
script z
return p
end script
set z to x's zOriginal
tell z to run
-- Result: "Script x"
Like I said, this is good behavior. When you read the script, the meaning of
the variable is based on the static meaning assigned at compile time (which
corresponds to what you see when you read the script) and not based on stuff
happening at other places as the script goes through the dynamics of its
execution.
So, how do you use this to your advantage? By being object oriented. I mean
really, really object oriented. Rabidly, dogmatically, religiously object
oriented.
- Everything is an object. Describe your script's behavior in terms of the
objects, what they do and what data they hold.
- Objects encapsulate data and function. Don't let outsiders mess with the
data, and don't define functions that work on other object's data.
In the example you gave,
>
script z --(z is inside x)
>
y's foo()
>
end script
you would have to change z so it doesn't go directly to y. The script y is
belongs to x--its part of x's data--so outsiders shouldn't interact with it.
Specifically, they shouldn't know that x has an object y with a handler foo().
It seems like you're trying to make z like a "plugin." You probably need to
define a more specific interface, defining what parameters x passes to the
plugin.
script x
property params : "Script x's data"
to invokePlugin(p)
tell p to getPlugged(params)
end tell
end script
script z
on getPlugged(interfaceData)
display dialog interfaceData
end getPlugged
end script
tell x to invokePlugin(z)
-- Dialog: "Script x's data"
The important part is that you make the interface between x and z explicit,
through the getPlugged(params) call. You could stick z into one of x's
properties, and perhaps x would decide when z needed to be called, but the
interface would still be explicit.
script x
property params : "Script x's data"
property plugin : {}
to doSomething()
-- stuff happens
tell plugin to getPlugged(params)
-- and more stuff
end tell
end script
script z
on getPlugged(interfaceData)
display dialog interfaceData
end getPlugged
end script
tell x to doSomething
-- Dialog: "Script x's data"
The advantages of this approach is that x has full control over its data, and z
has full control over its data, and the meet and agree at the interface. If x
changes its internal representation, it may need to calculate or generate
"params", but z never knows that. And z can use any variable names, knowing
that to get x's data, it looks in the explicit "params" argument, and not
implicit variable that x defines and z uses. Without the explicit interface,
you have all the evils of global variables: a diffuse interface that is spread
through both script objects, with the possibility of accidentally hijacking a
shared variable.
But because AppleScript uses static scoping, you can write "z" without knowing
what other objects, properties, or handlers "x" has, and you can change "x"
without having to rewrite "z" to match.
--
Scott Norton Phone: +1-703-299-1656
DTI Associates, Inc. Fax: +1-703-706-0476
2920 South Glebe Road Internet: email@hidden
Arlington, VA 22206-2768 or email@hidden