1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
|
<html>
<head>
<title>OTcl Tutorial (Version 0.96, September 95)</title>
</head>
<body>
<h1><a href="../README.html">OTcl</a> Tutorial (Version 0.96, September 95)</h1>
<p>This tutorial is intended to start you programming in OTcl quickly,
assuming you are already familiar with object-oriented programming. It
omits many details of the language that can be found in the reference
pages <a href="object.html">Objects in OTcl</a> and <a
href="class.html">Classes in OTcl</a>. It also doesn't mention the <a
href="capi.html">C API</a> or describe how to <a
href="autoload.html">autoload</a> classes.</p>
<h2>Comparison with C++</h2>
<p>To the C++ programmer, object-oriented programming in OTcl may feel
unfamiliar at first. Here are some of the differences to help you
orient yourself.</p>
<ul>
<li>Instead of a single class declaration in C++, write multiple
definitions in OTcl. Each method definition (with <tt>instproc</tt>)
adds a method to a class. Each instance variable definition (with
<tt>set</tt> or via <tt>instvar</tt> in a method body) adds an
instance variable to an object.
<li>Instead of a constructor in C++, write an <tt>init</tt> instproc
in OTcl. Instead of a destructor in C++, write a <tt>destroy</tt>
instproc in OTcl. Unlike constructors and destructors, init and
destroy methods do not combine with base classes automatically. They
should be combined explicitly with <tt>next</tt>.
<li>Unlike C++, OTcl methods are always called through the object. The
name <tt>self</tt>, which is equivalent to <tt>this</tt> in C++, may
be used inside method bodies. Unlike C++, OTcl methods are always
virtual.
<li>Instead of calling shadowed methods by naming the method
explicitly as in C++, call them with <tt>next</tt>. <tt>next</tt>
searches further up the inheritance graph to find shadowed methods
automatically. It allows methods to be combined without naming
dependencies.
<li>Avoid using static methods and variables, since there is no exact
analogue in OTcl. Place shared variables on the class object and
access them from methods by using <tt>$class</tt>. This behavior will
then be inherited. For inherited methods on classes, program with
meta-classes. If inheritance is not needed, use <tt>proc</tt> methods
on the class object.
</ul>
<h2>Programming in OTcl</h2>
<p>Suppose we need to work with many bagels in our application. We
might start by creating a <tt>Bagel</tt> class.</p>
<blockquote><pre>
% Class Bagel
Bagel
</pre></blockquote>
<p>We can now create bagels and keep track of them using the
<tt>info</tt> method.</p>
<blockquote><pre>
% Bagel abagel
abagel
% abagel info class
Bagel
% Bagel info instances
abagel
</pre></blockquote>
<p>Of course, bagels don't do much yet. They should remember whether
they've been toasted. We can create and access an instance variable
with the <tt>set</tt> method. All instance variables are public in the
sense of C++. Again, the <tt>info</tt> method helps us keep track of
things.</p>
<blockquote><pre>
% abagel set toasted 0
0
% abagel info vars
toasted
% abagel set toasted
0
</pre></blockquote>
<p>But we really want them to begin in an untoasted state to start
with. We can achieve this by adding an <tt>init</tt> instproc to the
<tt>Bagel</tt> class. Generally, whenever you want newly created
objects to be initialized, you'll write an <tt>init</tt> instproc for
their class.</p>
<blockquote><pre>
% Bagel instproc init {args} {
$self set toasted 0
eval $self next $args
}
% Bagel bagel2
bagel2
% bagel2 info vars
toasted
% bagel2 set toasted
0
</pre></blockquote>
<p>There are several things going on here. As part of creating
objects, the system arranges for <tt>init</tt> to be called on them
just after they are allocated. The <tt>instproc</tt> method added a
method to the <tt>Bagel</tt> class for use by its instances. Since it
is called init, the system found it and called it when a new bagel was
created.</p>
<p>The body of the <tt>init</tt> instproc also has some interesting
details. The call to <tt>next</tt> is typical for init methods, and
has to do with combining all inherited init methods into an aggregate
init. We'll discuss it more later. The variable called <tt>self</tt>
is set when a method is invoked, and contains the name of the object
on behalf of which it is running, or <tt>bagel2</tt> in this
case. It's used to reach further methods on the object or inherited
through the object's class, and is like <tt>this</tt> in C++. There
are also two other special variables that you may be interested in,
<tt>proc</tt> and <tt>class</tt>.</p>
<p>Our bagels now remember whether they've been toasted, except for
the first one that was created before we wrote an init. Let's destroy
it and start again.</p>
<blockquote><pre>
% Bagel info instances
bagel2 abagel
% abagel destroy
% Bagel info instances
bagel2
% Bagel abagel
abagel
</pre></blockquote>
<p>Now we're ready to add a method to bagels so that we can toast
them. Methods stored on classes for use by their instances are called
instprocs. They have an argument list and body like regular Tcl
procs. Here's the toast instproc.</p>
<blockquote><pre>
% Bagel instproc toast {} {
$self instvar toasted
incr toasted
if {$toasted>1} then {
error "something's burning!"
}
return {}
}
% Bagel info instprocs
init toast
</pre></blockquote>
<p>Aside from setting the <tt>toasted</tt> variable, the body of the
toast instproc demonstrates the <tt>instvar</tt> method. It is used to
declare instance variables and bring them into local scope. The
instance variable <tt>toasted</tt>, previously initialized with the
<tt>set</tt> method, can now be manipulated through the local variable
<tt>toasted</tt>.</p>
<p>We invoke the toast instproc on bagels in the same way we use the
<tt>info</tt> and <tt>destroy</tt> instprocs that were provided by the
system. That is, there is no distinction between user and system
methods.</p>
<blockquote><pre>
% abagel toast
% abagel toast
something's burning!
</pre></blockquote>
<p>Now we can add spreads to the bagels and start tasting them. If we
have bagels that aren't topped, as well as bagels that are, we may
want to make toppable bagels a separate class. Let explore inheritance
with these two classes, starting by making a new class
<tt>SpreadableBagel</tt> that inherits from <tt>Bagel</tt>.</p>
<blockquote><pre>
% Class SpreadableBagel -superclass Bagel
SpreadableBagel
% SpreadableBagel info superclass
Bagel
% SpreadableBagel info heritage
Bagel Object
</pre></blockquote>
<p>More options on the <tt>info</tt> method let us determine that
<tt>SpreadableBagel</tt> does indeed inherit from <tt>Bagel</tt>, and
further that it also inherits from <tt>Object</tt>. <tt>Object</tt>
embodies the basic functionality of all objects, from which new
classes inherit by default. Thus <tt>Bagel</tt> inherits from
<tt>Object</tt> directly (we didn't tell the system otherwise) while
<tt>SpreadableBagel</tt> inherits from <tt>Object</tt> indirectly via
<tt>Bagel</tt>.</p>
<p>The creation syntax, with its "-superclass", requires more
explanation. First, you might be wondering why all methods except
<tt>create</tt> are called by using their name after the object name,
as the second argument. The answer is that <tt>create</tt> is called
as part of the system's unknown mechanism if no other method can be
found. This is done to provide the familiar widget-like creation
syntax, but you may call <tt>create</tt> explicitly if you prefer.</p>
<p>Second, as part of object initialization, each pair of arguments is
interpreted as a (dash-preceded) procedure name to invoke on the
object with a corresponding argument. This initialization
functionality is provided by the <tt>init</tt> instproc on the
<tt>Object</tt> class, and is why the <tt>Bagel</tt> <tt>init</tt>
instproc calls <tt>next</tt>. The following two code snippets are
equivalent (except in terms of return value). The shorthand it what
you use most of the time, the longhand explains the operation of the
shorthand.</p>
<blockquote><pre>
% Class SpreadableBagel
SpreadableBagel
% SpreadableBagel superclass Bagel
</pre></blockquote>
<blockquote><pre>
% Class create SpreadableBagel
SpreadableBagel
% SpreadableBagel superclass Bagel
</pre></blockquote>
<blockquote><pre>
% Class SpreadableBagel -superclass Bagel
SpreadableBagel
</pre></blockquote>
<p>Once you understand this relationship, you will realize that there
is nothing special about object creation. For example, you can add
other options, such as one specifying the size of a bagel in
bites.</p>
<blockquote><pre>
% Bagel instproc size {n} {
$self set bites $n
}
% SpreadableBagel abagel -size 12
abagel
% abagel set bites
12
</pre></blockquote>
<p>We need to add methods to spread toppings to
<tt>SpreadableBagel</tt>, along with a list of current toppings. If we
wish to always start with an empty list of toppings, we will also need
an <tt>init</tt> instproc.</p>
<blockquote><pre>
% SpreadableBagel instproc init {args} {
$self set toppings {}
eval $self next $args
}
% SpreadableBagel instproc spread {args} {
$self instvar toppings
set toppings [concat $toppings $args]
return $toppings
}
</pre></blockquote>
<p>Now the use of <tt>next</tt> in the <tt>init</tt> method can be
further explained. <tt>SpreadableBagels</tt> are also bagels, and need
their <tt>toasted</tt> variable initialized to zero. The call to
<tt>next</tt> arranges for the next method up the inheritance tree to
be found and invoked. It provides functionality similar to
call-next-method in CLOS.</p>
<p>In this case, the <tt>init</tt> instproc on the <tt>Bagel</tt>
class is found and invoked. Eval is being used only to flatten the
argument list in <tt>args</tt>. When <tt>next</tt> is called again in
<tt>Bagels</tt> <tt>init</tt> instproc, the <tt>init</tt> method on
<tt>Object</tt> is found and invoked. It interprets its arguments as
pairs of procedure name and argument values, calling each in turn, and
providing the option initialization functionality of all
objects. Forgetting to call <tt>next</tt> in an <tt>init</tt> instproc
would result in no option initializations.</p>
<p>Let's add a taste instproc to bagels, splitting its functionality
between the two classes and combining it with <tt>next</tt>.</p>
<blockquote><pre>
% Bagel instproc taste {} {
$self instvar toasted
if {$toasted == 0} then {
return raw!
} elseif {$toasted == 1} then {
return toasty
} else {
return burnt!
}
}
% SpreadableBagel instproc taste {} {
$self instvar toppings
set t [$self next]
foreach i $toppings {
lappend t $i
}
return $t
}
% SpreadableBagel abagel
abagel
% abagel toast
% abagel spread jam
jam
% abagel taste
toasty jam
</pre></blockquote>
<p>Of course, along come sesame, onion, poppy, and a host of other
bagels, requiring us to expand our scheme. We could keep track of
flavor with an instance variable, but this may not be
appropriate. Flavor is an innate property of the bagels, and one that
can affect other behavior - you wouldn't put jam on an onion bagel,
would you? Instead of making a class heirarchy, let's use multiple
inheritance to make the flavor classes mixins that add a their taste
independent trait to bagels or whatever other food they are mixed
with.</p>
<blockquote><pre>
% Class Sesame
Sesame
% Sesame instproc taste {} {
concat [$self next] "sesame"
}
% Class Onion
Onion
% Onion instproc taste {} {
concat [$self next] "onion"
}
% Class Poppy
Poppy
% Poppy instproc taste {} {
concat [$self next] "poppy"
}
</pre></blockquote>
<p>Well, they don't appear to do much, but the use of <tt>next</tt>
allows them to be freely mixed.</p>
<blockquote><pre>
% Class SesameOnionBagel -superclass {Sesame Onion SpreadableBagel}
SesameOnionBagel
% SesameOnionBagel abagel -spread butter
% abagel taste
raw! butter onion sesame
</pre></blockquote>
<p>For multiple inheritance, the system determines a linear
inheritance ordering that respects all of the local superclass
orderings. You can examine this ordering with an <tt>info</tt>
option. <tt>next</tt> follows this ordering when it combines
behavior.</p>
<blockquote><pre>
% SesameOnionBagel info heritage
Sesame Onion SpreadableBagel Bagel Object
</pre></blockquote>
<p>We can also combine our mixins with other classes, classes that
need have nothing to do with bagels, leading to a family of chips.</p>
<blockquote><pre>
% Class Chips
Chips
% Chips instproc taste {} {
return "crunchy"
}
% Class OnionChips -superclass {Onion Chips}
OnionChips
% OnionChips abag
abag
% abag taste
crunchy onion
</pre></blockquote>
<h2>Other Directions</h2>
<p>There are many other things we could do with bagels, but it's time
to consult the reference pages. The OTcl language aims to provide you
with the basic object-oriented programming features that you need for
most tasks, while being extensible enough to allow you to customize
existing features or create your own.</p>
<p>Here are several important areas that the tutorial hasn't
discussed.</p>
<ul>
<li>There is support for autoloading libraries of classes and
methods. See <a href="autoload.html">OTcl Autoloading</a> for details.
<li>There is a C level interface (as defined by <tt>otcl.h</tt>) that
allows new objects and classes to be created, and methods implemented
in C to be added to objects. See <a href="capi.html">OTcl C API</a>
for details.
<li>Classes are special kinds of objects, and have all of the
properties of regular objects. Thus classes are a convenient
repository for procedures and data that are shared by their
instances. And the behavior of classes may be controlled by the
standard inheritance mechanisms and the class <tt>Class</tt>.
<li>Methods called procs can be added to individual object, for sole
use by that object. This allows particular objects to be hand-crafted,
perhaps storing their associated procedures and data.
<li>User defined methods are treated in the same way as system
provided methods (such as <tt>set</tt> and <tt>info</tt>). You can use
the standard inheritance mechanisms to provide your own implementation
in place of a system method.
<li>There are several other system methods that haven't been
described. <tt>array</tt> gives information on array instance
variables, <tt>unset</tt> removes instance variables, there are
further info options, and so forth.
</ul>
</body>
</html>
|