Version 4 of Snit design patterns

Updated 2003-08-17 17:43:03

See Design patterns in Tcl.

Started by Peter Lewerin (2003-08-17).

IANAPG (I am not a patterns guru), so I use the example code from http://www.dofactory.com/Patterns/Patterns.aspx as a base for my implementations.

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."

 package require snit

 snit::type handler {
         option -handleIf
         variable condition false
         onconfigure -handleIf value {set condition $value}

         option -handleBy 
         variable consequent {}
         onconfigure -handleBy value {set consequent $value}

         option -elseReportTo
         variable alternative {}
         onconfigure -elseReportTo value {
                 set alternative [concat $value handle \$request]
         }

         method handle request {
                 if $condition $consequent $alternative
         }
 }

Example:

 proc main args {
         handler Tom -handleIf {
                 $request >= 0 && $request < 10
         } -handleBy {
                 puts "$self handled request $request"
         } -elseReportTo Gus

         handler Gus -handleIf {
                 $request >= 10 && $request < 20
         } -handleBy {
                 puts "$self handled request $request"
         } -elseReportTo Mary

         handler Mary -handleIf {
                 $request >= 20 && $request < 30
         } -handleBy {
                 puts "$self handled request $request"
         }

         foreach request [list 2 5 14 22 18 38 3 27 20] {
                 Tom handle $request
         }
 }

Command: "Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations."

 package require snit

 snit::type command {
         option -calculator
         option -operator
         option -operand

         method execute {} {
                 [$self cget -calculator] operation [$self cget -operator] [$self cget -operand]
         }

         method unexecute {} {
                 set inverseOperator [string map {+ - - + * / / *} [$self cget -operator]]
                 [$self cget -calculator] operation $inverseOperator [$self cget -operand]
         }
 }

 snit::type receiver {
         variable accumulator 0

         method operation {operator operand} {
                 set accumulator [expr [list $accumulator $operator $operand]]
                 puts "Total = $accumulator (following $operator $operand)"
         }
 }

 snit::type invoker {
         variable calculator
         variable commands
         variable current 0

         method redo levels {
                 puts "---- Redo $levels levels"
                 for {set i 0} {$i < $levels} {incr i} {
                         if {$current < [llength $commands]} {
                                 [lindex $commands $current] execute
                                 incr current
                         }
                 }
         }

         method undo levels {
                 puts "---- Undo $levels levels"
                 for {set i 0} {$i < $levels} {incr i} {
                         if {$current > 0} {
                                 [lindex $commands [incr current -1]] unexecute
                         }
                 }
         }

         method compute {operator operand} {
                 set command [command %AUTO% \
                         -calculator $calculator \
                         -operator $operator \
                         -operand $operand]
                 $command execute
                 lappend commands $command
                 incr current
         }

         constructor args {
                 set calculator [receiver %AUTO%]
         }
 }

Example:

 proc main args {
         invoker user
         user compute + 100
         user compute - 50
         user compute * 10
         user compute / 2

         user undo 4
         user redo 3
 }