📄 00000004.htm
字号:
of a method that you want to invoke on the object. The object has the <BR>same name as its object command. Its data is stored in a global array, <BR>in this case a_color, but that is hidden from the programmer by the <BR>object command. <BR> <BR>We can now create as many objects as we want: just write a procedure <BR>like a1 for each object, replacing each occurrence of a1 by the name <BR>of another object. Sounds like a lot of work? It is. We will soon see <BR>how you can automate this. Writing a separate procedure for every object <BR> is not only tiresome; it also imposes heavy resource burdens on the <BR>application, because procedures take up space in the TCL interpreter. <BR> <BR>The first improvement is that we can write a single dispatcher procedure <BR> like this one: <BR> <BR> example3/apples.tcl <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>The object commands can now be written with only a single line of code: <BR> <BR> <BR> proc a1 {command args} { <BR> return [eval dispatch a1 $command $args] <BR> } <BR> <BR>Creating a procedure of this form for each object consumes less memory, <BR> simply because the procedure is shorter. But it is still quite <BR>cumbersome to write a procedure every time you want to instantiate an <BR>object. To simplify this task, we write yet another procedure, one <BR>that creates object commands! It looks like this: <BR> <BR> example4/apples.tcl <BR> proc apple {args} { <BR> foreach name $args { <BR> proc $name {command args} \ <BR> "return \[eval dispatch $name \$command \$args\]" <BR> } <BR> } <BR> <BR>We call this procedure the class command, because it is like a class <BR>type that you can instantiate. Instantiating and manipulating objects is <BR> now as simple as this: <BR> <BR> apple a1 a2 a3 <BR> a1 set_color green <BR> a2 set_color yellow <BR> a3 set_color red <BR> puts "a1 has color [a1 get_color]" <BR> puts "a2 has color [a2 get_color]" <BR> puts "a3 has color [a3 get_color]" <BR> <BR>The class command creates objects of class 'apple'. Each apple has its <BR>own color, which can be accessed through the methods get_color and <BR>set_color of the class. <BR> <BR>There are still some pieces missing in the puzzle. First of all, we <BR>now have a way of creating new objects, but we cannot delete objects <BR>yet. This leads to memory leaks, so we need to provide a procedure for <BR>deleting apples: <BR> <BR> example5/apples.tcl <BR> proc delete_apple {args} { <BR> global a_color <BR> foreach name $args { <BR> unset a_color($name) ; # Deletes the object's data <BR> rename $name {} ; # Deletes the object command <BR> } <BR> } <BR> <BR>We can also set up the array a_color in such a way that $a_color(obj) is <BR> always filled in for every object. We do this in the class command: <BR> <BR> proc apple {args} { <BR> foreach name $args { <BR> proc $name {command args} \ <BR> "return \[eval dispatch $name \$command \$args\]" <BR> set a_color($name) green <BR> } <BR> } <BR> <BR>This makes the class command act like a constructor that sets up the <BR>default values for object attributes. In this case we picked green as <BR>the default color. We now use the complete set of procedures like this: <BR> <BR> <BR> apple a1 a2 a3 <BR> a2 set_color yellow <BR> a3 set_color red <BR> puts "a1 has color [a1 get_color]" ; # Uses default color green <BR> puts "a2 has color [a2 get_color]" <BR> puts "a3 has color [a3 get_color]" <BR> delete a1 a2 a3 <BR> <BR>Summary <BR>To summarize, we have followed these steps: <BR> <BR>Store attributes in a global array <BR>Write a procedure for each 'method' of the object; this method takes the <BR> name of the object as its first argument. <BR>Write a dispatch procedure to call one of those methods. <BR>For each object, write a procedure (object command) with the same name <BR>as the object. Its first argument is the method name. It calls <BR>'dispatch'. <BR>For each class, write a procedure (class command) that creates the <BR>object commands automatically. The class command can also fill in <BR>default attribute values. <BR>For each class, write a delete procedure to reclaim resources of an <BR>object and destroy its object command. <BR>That's it. You now know enough to start using object commands and <BR>class commands in TCL. The rest of this paper offers a few more tips and <BR> tricks, plus (pointers to) real-life examples where object commands are <BR> used. <BR> <BR> <BR> <BR>------------------------------------------------------------------------ <BR>-------- <BR> <BR> <BR>More attributes <BR>We will give our apple class some more attributes, to show you how <BR>multiple attributes can be handled. We give each apple a size and a <BR>price (both are integers). These are again stored in global arrays, <BR>for example a_size and a_price. Both are indexed by the name of the <BR>object, just as for the a_color array we've been using so far. And again <BR> we can write get/set procedures to access these new attributes. The <BR>code is very similar to that for the color attribute, so I will not show <BR> it here. <BR> <BR>An interesting alternative is to use an array for every object, rather <BR>than an array for every attribute. TCL allows us to create a procedure <BR>and an array variable with the same name, so we can call our object <BR>command 'a1' and use an array 'a1' to store the attributes of that <BR>object. The code of all our procedures now changes slightly: <BR> <BR> example6/apples.tcl <BR> proc get_color {obj_name} { <BR> upvar #0 $obj_name arr <BR> return $arr(color) <BR> } <BR> <BR> proc set_color {obj_name color} { <BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -