Tcl Tutorial Lesson OOP2

More on object-oriented programming

Object-oriented programming is in fact a vast topic and each programming language that supports it puts its own emphasis on particular aspects. With Tcl you might say that it puts particular emphasis on objects being dynamic entities, just like the rest of Tcl.

Managing the object

For one thing, an object or a class may choose to export a method or unexport it. The rule that is used by default is:

  • If the method's name starts with an upper case letter it is private.
  • If the method's name starts with a lower case letter it is public.

But at any time you may change that:

    # Print is private by default ...
    oo::define myclass method Print {} {
        puts "This is the state of the object: ... (print variables)"
    }

    myclass create myobj

    # Export it just for this object
    oo::objdefine myobj {
        export Print
    }

    myobj Print

Objects continue to exist, unless you explicitly destroy them - in that respect they behave as builtin commands and procedures and not as variables. To destroy an object, call its destroy method - either the default, automatically constructed, method or the destructor defined for the class/object:

    myobj destroy

Trying to print the state of this object or do anything else with it after it has been destroyed would result in an error:

    % myobj Print
    invalid command name "myobj"
        while executing
    "myobj Print"

Extending classes

Extending classes can be done in various ways:

  • Defining new methods after the creation of the class, using the ::oo::define command
  • Inheritance - create a new class based on one or more existing classes
  • Defining mixin classes - the methods of such a class become available in the original class

For example, you could define a class that prints out the data members of the object and then mix it into the definition of another class:

    oo::class create printable {
        method print {} {
            foreach v [info class variables [info object class [self]]] {
                my variable $v
                puts "$v: [set $v]"
            }
        }
    }

    oo::class create counter {
        variable count
        constructor {} {
            set count 0
        }
        method incr {} {
            incr count
        }
        mixin printable
    }

    counter create countup

    countup incr
    countup incr
    countup print

which prints, as expected:

count: 2

In the above fragment we use several introspection features for interrogating the object and the class it belongs to. There are many more facilities available - see the online chapter of Ashok's book.

Using filters

Yet another feature to influence the behaviour of an object or a class of objects is the use of filters. Basically it is a method that is called before the actual method that you want to call. A simple example is this: to track down where a particular object is being used, you introduce a filter method:

    oo::objdefine countup {
        method Log args {
            my variable count
            puts "Called in [lindex [info level 1] 0] ... counter was: $count"
            return [next {*}$args]
        }
        filter Log
    }

    proc runCalculation {} {
        countup incr
        countup incr
        puts "Done"
    }

    runCalculation

gives:

count: 2
Called in runCalculation ... counter was: 2
Called in runCalculation ... counter was: 3
Done

The next command makes it possible to delegate the work to the next method in a potentially long chain of commands. Just make sure that next command has all the arguments it needs.

(Note: we look at level 1, not level 0, to get to the caller of the incr method of countup)

With these and other facilities at hand you can build a program that takes advantage of well-known object-oriented programming techniques as well the dynamic character of Tcl. The main pitfall is that you can make it too sophisticated, so that it becomes increasingly difficult to understand what is going on.

All in all, TclOO provides a wide spectrum of features that enable different styles of object-oriented programming.