📄 00000004.htm
字号:
created. <BR> set template { <BR> proc @classname@ {obj_name args} { <BR> # The class command in turn creates an object command. <BR> # Fewer escape characters thanks to the '@' sign. <BR> proc $obj_name {command args} \ <BR> "return \[eval dispatch_@classname@ $obj_name \$command <BR>\$args\]" <BR> <BR> # Set variable defaults <BR> upvar #0 $obj_name arr <BR> array set arr {@vars@} <BR> <BR> # Then possibly override those defaults with user-supplied <BR>values <BR> if { [llength $args] > 0 } { <BR> eval $obj_name configure $args <BR> } <BR> } <BR> } <BR> <BR> regsub -all @classname@ $template $classname template <BR> regsub -all @vars@ $template $vars template <BR> <BR> eval $template <BR> <BR> # Create the dispatcher, which dispatches to one of the class <BR>methods <BR> set template { <BR> proc dispatch_@classname@ {obj_name command args} { <BR> upvar #0 $obj_name arr <BR> if { $command == "configure" || $command == "config" } { <BR> array set arr $args <BR> <BR> } elseif { $command == "cget" } { <BR> return $arr([lindex $args 0]) <BR> <BR> } else { <BR> if { [lsearch {@methods@} $command] != -1 } { <BR> uplevel 1 @classname@_${command} $obj_name $args <BR> } else { <BR> puts "Error: Unknown command $command" <BR> } <BR> } <BR> } <BR> } <BR> <BR> regsub -all @classname@ $template $classname template <BR> regsub -all @methods@ $template $methods template <BR> <BR> eval $template <BR> } <BR> <BR>You see that this simplifies the code. We use the '@' sign because it is <BR> not frequently used in normal TCL code. We postpone the evaluation of <BR>$classname and other variables until we are out of the inner procedure <BR>body, so that the number of escape characters is reduced to almost zero. <BR> <BR> <BR>With or without this "template" technique, we can now create our <BR>original classes apple and fridge in a more compact way: <BR> <BR> example12/classes.tcl <BR> class apple {-color green -size 5 -price 10} {byte} <BR> proc apple_byte {self} { <BR> upvar #0 $self arr <BR> puts "Taking a byte from apple $self" <BR> incr arr(-size) -1 <BR> if { $arr(-size) <= 0 } { <BR> puts "Apple $self now completely eaten! Deleting it..." <BR> delete $self <BR> } <BR> } <BR> <BR> class fridge {-state closed -label A} {open close} <BR> proc fridge_open {self} { <BR> upvar #0 $self arr <BR> if { $arr(-state) == "open" } { <BR> puts "Fridge $self already open." <BR> } else { <BR> set arr(-state) "open" <BR> puts "Opening fridge $self..." <BR> } <BR> } <BR> <BR> proc fridge_close {self} { <BR> upvar #0 $self arr <BR> if { $arr(-state) == "closed" } { <BR> puts "Fridge $self already closed." <BR> } else { <BR> set arr(-state) "closed" <BR> puts "Closing fridge $self..." <BR> } <BR> } <BR> <BR>There are several things to note in this implementation: <BR> <BR>Creating new classes is indeed a lot simpler than before. We only need <BR>one line with the class "declaration", plus one proc for each of the <BR>class methods. <BR>Each method is implemented as a global proc which has the instance <BR>name as its first argument. Any other arguments are optional. <BR>In the implementation of each method, we access the object's array <BR>directly. We could make the methods less dependent on the actual <BR>implementation of the object by using configure and cget instead, for <BR>example <BR> example13/classes.tcl <BR> proc fridge_close {self} { <BR> if { [$self cget -state] == "closed" } { <BR> puts "Fridge $self already closed." <BR> } else { <BR> $self configure -state "closed" <BR> puts "Closing fridge $self..." <BR> } <BR> } <BR> <BR>This is less implementation-dependent, and perhaps slightly more <BR>readable. It is less efficient though, because the configure and cget <BR>implementations add an extra level of procedure calls with a couple of <BR>ifs. You should probably decide for yourself which of the two ways you <BR>are going to use, depending on the importance of efficiency in your <BR>application. <BR>Also note that we can implement the class procedure in a slightly <BR>different way, without actually knowing in advance the list of all the <BR>variables and methods of the class. The new implementation could look <BR>like this: <BR> example14/classes.tcl <BR> # No more 'methods' argument here; 'vars' is optional <BR> proc class {classname {vars ""}} { <BR> <BR> # Create the class command, which will allow new instances to <BR>be created. <BR> set template { <BR> proc @classname@ {obj_name args} { <BR> # The class command in turn creates an object command. <BR> # Fewer escape characters thanks to the '@' sign. <BR> proc $obj_name {command args} \ <BR> "return \[eval dispatch_@classname@ $obj_name <BR>\$command \$args\]" <BR> <BR> # Set variable defaults, if any <BR> upvar #0 $obj_name arr <BR> @set_vars@ <BR> <BR> # Then possibly override those defaults with <BR>user-supplied values <BR> i
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -