[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... [Peter Lewerin] (2003-08-17) if I can keep it up, I'll try to provide examples in [Snit]: [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 '''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 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} set index 0 foreach level $::debugLevels { proc errlog-$level {body} [string match [list IDX $index] { if {[lsearch $::debugLevels $::config(debugLevel)]>=IDX} {uplevel $body} }] } Then you might do things like this: if {[catch {[db connect $mydb $myuser $mypassword]} errMsg]} { errlog-WARNING {log2file "Could not connect to $mydb"} # more precise diagnostic when debug level is set to INFO: errlog-INFO {log2file "$errMsg when trying to connect to $mydb with user $myuser (pass: $mypassword)"} # return error msg return -code error "failed" } * Document processing in multiple languages (light content management) Here is a true 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. How did I do this? Here it is: 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 English and French contents. ---- [Arts and crafts of Tcl-Tk programming] - [Playing UML] - [Category Design]