Design patterns in Tcl

APN Also see design-patterns-in-tcl - a collection of popular design patterns implemented in TCL language with TclOO package.

Richard Suchenwirth - Design patterns have been made famous by the book "Design Patterns. Elements of Reusable Object-Oriented Software" by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (The Gang of Four). A short list of the 23 patterns in that book is at http://wiki.cs.uiuc.edu/PatternStories/DesignPatterns , from where the "Intent" phrases are quoted below.

Most patterns assume OO approaches, inheritance, etc., and thus are more suited for Incr Tcl design patterns. But some can be done in pure Tcl, or are used in the Tcl implementation. Let's brainstorm...

See also Snit design patterns.

Chain of responsibility: "Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it." We can have that effect with

 rename existingName _otherName
 proc existingName {...} {
    # do the special stuff, eventually:
    _otherName
 }

See Overloading widgets, Guarded proc for concrete examples. NEM - This runs into problems if it is applied more than once:

 rename exisingName _otherName
 proc existingName {...} {...}
 ...
 rename exisingName _otherName ;# Oops! "Command already exists"!

A method of overriding a command without renaming the original would be good. In a message-passing environment, this can be done (e.g. see XOTcl), but in pure Tcl I cannot see a general solution. But see Stacking Commands for a suggestion. --[LeRoi] Or stacking.

Argument filters When you want to validate a parameter (is it a number? an enumeration type? a complex list formed by many types?) you can write :

 proc add {a b} {
     _checkNumber a b
     ... do the things right, assuming a and b are numbers ...
 }
 # a simple example
 proc _checkNumber {args} {
     foreach varname $args {
         upvar $varname number
         if {![string is digit $number]} {error "argument is not a number : $number"}
     }
 }

Command: "Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations." Not sure (especially on the "undo" aspect), but isn't the following already a simple implementation?

 set cmd [list keyword arg arg...]
 # some time later:
 eval $cmd

A command prefix may be a more general implementation of this.

Composite: "Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly." Tcl: lists of lists...

 proc handleComposite c {
    if {[llength $c]>1} {
        foreach i $c {
            handleComposite $i
        }
    } else {
        #individual object handler
    }
 }

Flyweight: "Use sharing to support large numbers of fine-grained objects efficiently." Re-use of small objects (Tcl_Objs) from a pool, so distinct objects are only created once, and referred to later. Used since 8.4 in the C implementation of split $string ""

To some extent this idea is behind Tcl's "copy on write" strategy for sharing Tcl_Obj values as well.

The Hugelist uses this pattern. See Mass-widget for the design issue.

Iterator: "Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation." We have that sort-of built-in for the major container kinds (lists and arrays):

 foreach i           $list           {...}
 foreach {key value} [array get A]   {...}
 foreach key         [array names A] {...} 

Alternatively there are the [array] subcommands:

 array anymore
 array donesearch
 array nextelement
 array startsearch

Observer: "Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. " Why, if that isn't

 trace variable X w {...}

TV I can't escape the, positively intended, notion that all of programming world would not be bad of with a good idea of what unix-like processes and system programming have done already for I guess 25 years or so. Memory management, processes, signals, semaphores, process groups, streams especially, and knowing what timing and scheduling does on the processor/machine level are the subjects involved in many problems which seem to, as it would seem to me, young or unaware programmers to be a sort of 'fundamental' problems in OO or semi-formal languages, while in fact it all comes from completely technical considerations based directly on the most common computer architecture: the one with a processor core and one linear main memory for data and programs.

Not noting this makes one write programs which do all of such things so inefficiently and at such high level of agregation that programs become bigger and bigger, and so slow it isn't funny anymore for even relatively simply tasks, and that uninformed people might be deluded to think that is the Actual State of the Holy Programming Art Worldwide, as it is, or something, which gives room to all kinds of rather insipid logics and thought and behaviour patterns to sprout up.

I find this at least an interesting and enlighting (in the positive sense of the expression..) page in that context.

In objective-C you'd have ordered and unordered collections, I guess that is an interesting concept in the 'foreach' use pattern.

Switcher: "Allowing different behaviors upon a switched variable, debug on/off, GUI on/off, and so on..."

  • GUI on/off
 set ::config(gui) on
 proc with-gui {body} {
     if {$::config(gui)} {return [uplevel $body]}
     return
 }
 proc no-gui {body} {
     if {!$::config(gui)} {return [uplevel $body]}
     return
 }

Then, you should be able to put together two applications in the same: one with a GUI, and one with text interface.

  • debug on/off
 set ::config(debugLevel) TRACE
 set ::debugLevels {TRACE INFO WARNING ERROR FATAL}
 proc log2file {level message} {
     puts "$level : $message"
 }
 # creation of debug procs
 foreach level $::debugLevels {
     proc errlog-$level {value} [string map [list LEVEL $level] {
         if {[lsearch $::debugLevels $::config(debugLevel)]<=[lsearch $::debugLevels LEVEL]} {
             log2file LEVEL [uplevel subst [list $value]]
         }
     }]
 }

 # example of use : error handling of a connection to a database
 if {[catch {[db connect $mydb $user $password]} errMsg]} {
     errlog-WARNING {Could not connect to $mydb}
     # more precise diagnostic when debug level is set to INFO:
     errlog-INFO {$errMsg when trying to connect to $mydb with user $user (pass: $password)}
     # return error msg
     return -code error "failed"
 }
  • Document processing in multiple languages (light content management)

Here is a real example: with textutil::expander, I preprocessed html files. The content had to be in two different languages and I wanted it to be hold in the same file. But I did not want to pollute code with if statements.

Here is what I did:

 proc francais {body} {
     if {$::config(langue) eq "fr"} {return [uplevel $body]}
     return
 }
 proc anglais {body} {
     if {$::config(langue) eq "en"} {return [uplevel $body]}
     return
 }
 proc macro {nom arguments corps} {
     proc ${nom}-fr $arguments "francais [list $corps]"
     proc ${nom}-en $arguments "anglais [list $corps]"
 }
 macro titre {titre} {
     ::myexpander cset titre [htmlencode $titre]
 }

Then, in my template, I put the title in two different languages:

 [titre-en {Arbitray-precision floating-point numbers}]
 [titre-fr {Nombres en virgule flottante de précision arbitraire}]

and the same file was produced as both, English and French, contents.


EKB Steve Yegge wrote (IMHO) an interesting article [L1 ] on what he calls the "Prototype Pattern". He argues that it is more properly a modeling approach, like class modeling and relational modeling. I did a basic implementation of the Prototype Pattern in Tcl.