**Object-oriented programming 101** !!!!!! '''[Tcl Tutorial Lesson 43%|%Previous lesson%|%]''' | '''[Tcl Tutorial Index%|%Index%|%]''' | '''[Tcl Tutorial Lesson OOP2%|%Next lesson%|%]''' !!!!!! Since version 8.6 Tcl has a framework for object-oriented programming, meant both as a full-fledged OOP system and as the basis for more extensive OOP systems. This is part of a long tradition: Tcl has long been flexible enough for people to build their own OOP system on top of, leading to a plethora of such systems, with very diverse functionality, to name a few: IncrTcl, XoTcl and Snit. Here we take a look at the basic OOP features. Much more can be found for instance in the book ''The Tcl Programming Language'' by Ashok Nadkarni, or in the [http://www.magicsplat.com/articles/oo.html%|%online chapter%|%]. Consider the following code: ====== proc range {cmd {arg {}}} { global RANGE switch -- $cmd { "max" { set RANGE(max) $arg set RANGE(current) -1 } "next" { upvar $arg var incr RANGE(current) set var $RANGE(current) if { $var < $RANGE(max) } { return 1 } else { return 0 } } } } ====== This command is meant to generate a series of integer values, so that we can iterate over them. This could be quite useful in a numerical context. (Of course we can also use the `for` command.) We can use it like this: ====== range max 5 while { [range next idx] } { puts $idx } ====== and produce the numbers 0, 1, ... 4. While the implementation above does not take care of error handling for simplicity - normally you should make your code robust enough - it has a much larger flaw: you can not use it in a "nested" way because of the use of a global array (`RANGE`) to store the state. You might be able to avoid that in your own code, but what if you were to use a library from somebody else that also uses the command? Try (intended is of course to have x and y both run from 0 to 9): ====== range max 10 while { [range next x] } { while { [range next y] } { puts [expr {$x + $y}] } } ====== That is where object-oriented programming techniques come in. You can store the state inside an "object" and this state is not easily accessible outside the interface to the object. So, here is one way you can turn the above `range` command into a ''class'' to derive objects from: ====== ::oo::class create range { variable max current constructor {upto} { my max $upto } method max {upto} { variable max current set max $upto set current -1 } method next {varName} { upvar $varName var incr current set var $current if { $var < $max } { return 1 } else { return 0 } } } ====== The code contains the two subcommands from the original `range` command, but also a constructor. This method is called when the object is created. To avoid duplication of code it calls the `max` method on itself. Hence it is called via the `my` command. An important difference is that each method has its own argument list, whereas with the original `range` command the argument list has to work for all subcommands and so is rather abstract. In a command with limited functionality this is not that bad, but suppose you have 10 different subcommands. The logic would get quite involved. With this class we can create any number of ''range'' objects that work independently: ====== set xrange [range new 10] set yrange [range new 10] while { [$xrange next x] } { while { [$yrange next y] } { puts [expr {$x + $y}] } $yrange max 10 ;# We need to reset the y-range } ====== The command `$yrange max 10` is necessary to reset the `yrange` object. Otherwise it would simply keep returning 0 and nothing would happen. This is a bit awkward, so let us add a command that resets the range objects. We can either extend the class or add it via the `oo:define` feature: ====== ::oo::define range method reset {} {set current -1} ====== This causes a new method, `reset`, to become available for ''all instances'' of this class, even the ones that have already been created at that point. TclOO, as this feature is commonly known, also allows for inheritance and even multiple inheritance. As an alternative we could create an expanded class which has the `reset` method: ====== ::oo::class create newrange { superclass range method reset {} {set current -1} } ====== The `superclass` command arranges that the new class inherits the methods and variables from the class `range`. If you want multiple inheritance, list two or more classes as arguments to this command. Sometimes it is useful to create an object with a specific name. Use `className create` in stead of `className new`: ====== range create xrange 10 ====== creates an object that van be invoked as `xrange` instead of `$xrange`. Another useful feature is that you can clone an object: ====== ::oo::copy xrange yrange ====== create a new object, `yrange` with the same methods and variables that have the same values as the object `xrange`. TclOO thus can be used in a similar fashion as class in C++ or Java, but there is more. You can add methods and variables to an object instead of a class. In that case, the object has the original methods and variables, just as any object from that class, but also extra methods and variables, possibly unique to the object: ====== ::oo::objdefine $yrange { method reset {} { my variable current puts "resetting ..." set current -1 } } ====== Due to the scoping rules, which become somewhat involved with inheritance and mixins, you need to explicitly import the object variables via the `my variable` command, like shown above. The `variable` command that is used within the context of namespaces performs a similar but not identical task. Further features that are worth exploring are: * The use of mixins, classes that are "mixed into" the object * Filters that can be interposed in the method calls * Introspection via the `self` command * Invoking methods in the superclasses These topics and others are summarised in [Tcl Tutorial Lesson OOP2%|%More on Object-oriented programming|%]. !!!!!! '''[Tcl Tutorial Lesson 43%|%Previous lesson%|%]''' | '''[Tcl Tutorial Index%|%Index%|%]''' | '''[Tcl Tutorial Lesson OOP2%|%Next lesson%|%]''' !!!!!!