📄 00000004.htm
字号:
upvar #0 $obj_name arr <BR> set arr(color) $color <BR> } <BR> <BR> proc dispatch {obj_name command args} { <BR> if { $command == "get_color" } { <BR> return [get_color $obj_name] <BR> } elseif { $command == "set_color" } { <BR> set_color $obj_name [lindex $args 0] <BR> } else { <BR> puts "Error: Unknown command $command" <BR> } <BR> } <BR> <BR> proc apple {args} { <BR> foreach name $args { <BR> proc $name {command args} \ <BR> "return \[eval dispatch $name \$command \$args\]" <BR> upvar #0 $name arr <BR> set arr(color) green <BR> } <BR> } <BR> <BR> proc delete_apple {args} { <BR> foreach name $args { <BR> upvar #0 $name arr <BR> unset arr ; # Deletes the object's data <BR> rename $name {} ; # Deletes the object command <BR> } <BR> } <BR> <BR> # Note the advantage of using an array per object: <BR> # 'delete_apple' can just 'unset arr' instead of having to <BR> # remove one entry in three different arrays. <BR> <BR>A third alternative is to use only a single, global array, indexed by <BR>the object name and the attribute name. To find the color of apple a1, <BR>you would have to access $attributes(a1,color). The advantage of <BR>having only a single array to maintain, has to be weighed off against <BR>the disadvantage of having to delete several array entries in the <BR>delete_apple procedure. <BR> <BR>Configuring the attributes <BR>Another improvement that we can make, is to get rid of all those <BR>annoying get/set methods. We do this by introducing two new methods <BR>for each class, called configure and cget. The first gives new values to <BR> some attributes, the second reads the value of an attribute. We can <BR>implement these procedures for the apple class as follows: <BR> <BR> proc dispatch {obj_name command args} { <BR> upvar #0 $obj_name arr <BR> if { $command == "configure" || $command == "config" } { <BR> foreach {opt val} $args { <BR> if { ![regexp {^-(.+)} $opt dummy small_opt] } { <BR> puts "Wrong option name $opt (ignored)" <BR> } else { <BR> set arr($small_opt) $val <BR> } <BR> } <BR> <BR> } elseif { $command == "cget" } { <BR> set opt [lindex $args 0] <BR> if { ![regexp {^-(.+)} $opt dummy small_opt] } { <BR> puts "Wrong or missing option name $opt" <BR> return "" <BR> } <BR> return $arr($small_opt) <BR> <BR> } elseif { $command == "byte" } { <BR> puts "Taking a byte from apple $obj_name ($arr(size))" <BR> incr arr(size) -1 <BR> if { $arr(size) <= 0 } { <BR> puts "Apple $obj_name now completely eaten! Deleting it... <BR>" <BR> delete_apple $obj_name <BR> } <BR> <BR> } else { <BR> puts "Error: Unknown command $command" <BR> } <BR> } <BR> <BR> # We also change the implementation of the "constructor", <BR> # so that it accepts initializing values for the attributes. <BR> proc apple {name args} { <BR> proc $name {command args} \ <BR> "return \[eval dispatch $name \$command \$args\]" <BR> <BR> # First set some defaults <BR> upvar #0 $name arr <BR> set arr(color) green <BR> set arr(size) 5 <BR> set arr(price) 10 <BR> <BR> # Then possibly override those defaults with user-supplied <BR>values <BR> if { [llength $args] > 0 } { <BR> eval $name configure $args <BR> } <BR> } <BR> <BR>Attribute access now looks exactly as it does for Tk widgets. Compare <BR>these two fragments of code: <BR> <BR> button .b -text "Hello" -command "puts world" <BR> .b configure -command "exit" <BR> set textvar [.b cget -text] <BR> <BR> apple a -color red -size 5 <BR> a configure -size 6 <BR> set clr [a cget -color] <BR> <BR>Some widget libraries that are written in pure TCL, use object <BR>commands and configure/cget methods to make the widget syntax the same <BR>as in Tk. But obviously, this technique also works for other kinds of <BR>objects. <BR> <BR>Object persistence <BR>We will now cover a more exotic topic: object persistence. This means <BR>that you can save an object on disk, and recover it later, in the same <BR>or in another application, even in another process. The recovered object <BR> has exactly the same attributes as the one you saved. <BR> <BR>In languages such as C++, object persistence is quite a challenge <BR>(especially if you want to save an object on one platform, and recover <BR>it on another platform with different endianness or with a different <BR>compiler). But the flexibility of TCL makes object persistence a piece <BR>of cake! We will save our objects in a text file, then treat that file <BR>as an Active File to read the objects back (Read more about the Active <BR>File pattern in my paper on TCL file formats, or on >> Nat Pryce's web <BR>site). <BR> <BR>We only need a single Tcl procedure (!) to give objects of all classes <BR>the ability to make themselves persistent: <BR> <BR> example8/apples.tcl <BR> proc write_objects {classname args} { <BR> foreach name $args { <BR> upvar #0 $name arr <BR> puts "$classname $name \\" <BR> foreach attr [array names arr] { <BR> puts " -$attr $arr($attr) \\" <BR> } <BR> puts "" <BR> } <BR> } <BR> <BR>The idea is that this procedure is invoked as follows: <BR> <BR> write_objects apple a1 a2 a3 <BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -