📄 00000004.htm
字号:
<BR>The implementation above shows that the procedure makes the objects a1, <BR> a2, a3 of class 'apple' persistent, by simply outputting a call to <BR>the class command 'apple' followed by the object name and all its <BR>attributes. The resulting output is stored in a file and looks like <BR>this: <BR> <BR> apple a1 \ <BR> -price 10 \ <BR> -size 5 \ <BR> -color green \ <BR> <BR> apple a2 \ <BR> -price 10 \ <BR> -size 3 \ <BR> -color yellow \ <BR> <BR> apple a3 \ <BR> -price 12 \ <BR> -size 5 \ <BR> -color red \ <BR> <BR>It is now extremely easy to read these persistent objects back from <BR>disk: just source the file! The source command executes all class <BR>commands in the file, creating instances with exactly the same <BR>attributes as the ones we saved earlier. Object persistence in Tcl is <BR>indeed a piece of cake. <BR> <BR>Adding new classes <BR>So far, we have worked with only a single class apple. If we want to add <BR> a new class to our example, we need to write a new class command and <BR>a new dispatcher procedure. <BR>Suppose we also want to have objects of class fridge (in which we will <BR>want to store apples of course). We need to duplicate the effort we <BR>did on the apple class: <BR> <BR> example10/classes.tcl <BR> proc dispatch_fridge {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> } elseif { $command == "open" } { <BR> if { $arr(-state) == "open" } { <BR> puts "Fridge $obj_name already open." <BR> } else { <BR> set arr(-state) "open" <BR> puts "Opening fridge $obj_name..." <BR> } <BR> <BR> } elseif { $command == "close" } { <BR> if { $arr(-state) == "closed" } { <BR> puts "Fridge $obj_name already closed." <BR> } else { <BR> set arr(-state) "closed" <BR> puts "Closing fridge $obj_name..." <BR> } <BR> <BR> } else { <BR> puts "Error: Unknown command $command" <BR> } <BR> } <BR> <BR> proc fridge {name args} { <BR> proc $name {command args} \ <BR> "return \[eval dispatch_fridge $name \$command \$args\]" <BR> <BR> # First set some defaults <BR> upvar #0 $name arr <BR> array set arr {-state closed -label A} <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>This laborious task can also be partly automated by a procedure called <BR>class which accepts the name of a new class, a list of its member <BR>variables, and a list of its method names. It then automatically sets up <BR> the necessary procedures such as the class command and the dispatcher <BR>proc. The only thing we still need to implement by hand, are the methods <BR> of the class. The whole thing could be set up as follows: <BR> <BR> example11/classes.tcl <BR> proc class {classname vars methods} { <BR> <BR> # Create the class command, which will allow new instances to be <BR>created. <BR> proc $classname {obj_name args} " <BR> # The class command in turn creates an object command. <BR>Careful <BR> # with those escape characters! <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> # Create the dispatcher, which dispatches to one of the class <BR>methods <BR> proc dispatch_$classname {obj_name command args} " <BR> upvar #0 \$obj_name arr <BR> if { \$command == \"configure\" || \$command == \"config\" } <BR>{ <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>The class procedure basically just creates two new commands for us (a <BR>class command and a dispatcher). <BR> <BR>The code looks pretty messy, because it contains two levels of <BR>indirection: a proc that creates a proc that creates yet another proc. <BR>This involves a bit of backslash-escape sourcery, which can be <BR>confusing. Richard Suchenwirth has a very nice solution to make this <BR>kind of proc-creating-proc more readable: he creates a template with <BR>names containing a special character such as the '@' sign; then he <BR>replaces those names by the actual class and instance names, using <BR>regsub. See his page on >> gadgets for an example. Using this technique, <BR> our implementation becomes a lot simpler: <BR> <BR> example12/classes.tcl <BR> proc class {classname vars methods} { <BR> <BR> # Create the class command, which will allow new instances to be <BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -