?? ch14.htm
字號:
the program's output. This technique avoids having to call the
<TT>bless()</TT> fuNCtion in the base
class constructor.
<P>
By now, you must be wondering where polymorphism fits into this
example. Well, the simple fact that both the <TT>Pen</TT>
and <TT>Inventory_item</TT> classes
have the <TT>qtyChange()</TT> method
means that polymorphism is being used. While the <TT>Inventory_item::qtyChange()</TT>
method defaults to changing the quantity by one, the <TT>Pen::qtyChange()</TT>
method defaults to changing the quantity by 100. Because the <TT>Pen::qtyChange()</TT>
method simply modifies the behavior of <TT>Inventory_item::qtyChange()</TT>,
it does not need to know any details about how the quantity is
actually changed. This capability to change fuNCtionality without
knowing the details is a sign that abstraction is taking place.
<BR>
<p>
<CENTER>
<TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR><TD><B>Tip</B></TD></TR>
<TR><TD>
<BLOCKQUOTE>
The <TT>Inventory_item::qtychange()</TT> notation refers to the <TT>qtyChange()</TT> fuNCtion in the <TT>Inventory_item</TT> class, and <TT>Pen::qtyChange()</TT> refers to the <TT>qtyChange()</TT> fuNCtion in the <TT>Pen</TT> class. This notation lets you
uniquely identify any method in your script.
</BLOCKQUOTE>
</TD></TR>
</TABLE>
</CENTER>
<P>
<H3><A NAME="ExampleHowOneClassCanContainAnother">
Example: How One Class Can Contain Another</A></H3>
<P>
Now that you have seen several objects in action, you probably
realize that some class properties will be objects themselves.
For example, you might have a billing object that contains an
inventory object, or you might use a car object inside a warehouse
object. The possibilities are endless.
<P>
Listing 14.5 shows how to add a color object to the inventory
system you've been building. It also shows you that Perl will
execute statements that are not part of a fuNCtion-even those
in packages other than main-as soon as they are seen by the interpreter.
<P>
<IMG SRC="pseudo.gif" tppabs="http://cheminf.nankai.edu.cn/~eb~/Perl%205%20By%20Example/pseudo.gif" BORDER=1 ALIGN=RIGHT><p>
<BLOCKQUOTE>
<I>Start a definition of the </I><TT><I>Inventory_item</I></TT><I>
class.<BR>
Define the constructor for the class.<BR>
Get the name of the class from the parameter array.<BR>
Assign the rest of the parameters to the </I><TT><I>%params</I></TT><I>
hash.<BR>
Bless the anonymous hash with the class name.<BR>
Use </I><TT><I>%params</I></TT><I>
to initialize the class properties.<BR>
Start a definition of the </I><TT><I>Pen</I></TT><I>
class.<BR>
Initialize the </I><TT><I>@ISA</I></TT><I>
array to define the parent classes.<BR>
Define the constructor for the class.<BR>
Get the name of the class from the parameter array.<BR>
Assign the rest of the parameters to the </I><TT><I>%params</I></TT><I>
hash.<BR>
Call the constructor for the parent class and assign the resulting
object refereNCe to </I><TT><I>$self</I></TT><I>.
<BR>
Create an entry in the anonymous hash for the </I><TT><I>INK_COLOR</I></TT><I>
key by calling the constructor for the </I><TT><I>Color</I></TT><I>
class.<BR>
Return a refereNCe to the anonymous hash that has been blessed
into the </I><TT><I>Pen</I></TT><I>
class.<BR>
Start a definition of the </I><TT><I>Color</I></TT><I>
class.<BR>
Print a message on </I><TT><I>STDOUT</I></TT><I>.
<BR>
Create two entries in the </I><TT><I>%Colors</I></TT><I>
hash.<BR>
Define the constructor for the class.<BR>
Get the name of the class from the parameter array.<BR>
Assign the rest of the parameters to the </I><TT><I>%params</I></TT><I>
hash.<BR>
Assign a refereNCe to one of the entries in the </I><TT><I>%Colors</I></TT><I>
hash to </I><TT><I>$self</I></TT><I>.
This will be used as the object refereNCe.<BR>
Bless the hash entry into the </I><TT><I>Color</I></TT><I>
class and return </I><TT><I>$self</I></TT><I>
as the object refereNCe.<BR>
Start the </I><TT><I>main</I></TT><I>
namespace.<BR>
Print a message on </I><TT><I>STDOUT</I></TT><I>.
<BR>
Call the constructor for the </I><TT><I>Pen</I></TT><I>
class. Assign the object refereNCe to </I><TT><I>$item</I></TT><I>.
<BR>
Use </I><TT><I>%properties</I></TT><I>
as a temporary value to simplify the derefereNCing process.<BR>
Print the three property values to verify that the property initialization
worked.</I>
</BLOCKQUOTE>
<HR>
<BLOCKQUOTE>
<B>Listing 14.5 14LST05.PL-How One Class Can Use or
Contain Another Class<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<PRE>
package Inventory_item;
sub new {
my($class) = shift;
my(%params) = @_;
bless {
"PART_NUM" => $params{"PART_NUM"},
"QTY_ON_HAND" => $params{"QTY_ON_HAND"}
}, $class;
}
package Pen;
@ISA = (Inventory_item);
sub new {
my($class) = shift;
my(%params) = @_;
my($self) = Inventory_item->new(@_);
$self->{"INK_COLOR"} = Color->new($params{"INK_COLOR"});
return(bless($self, $class));
}
package Color;
print("Executing Color statements\n");
$colors{"blue"} = "Die Lot 13";
$colors{"red"} = "Die Lot 5";
sub new {
my($class) = shift;
my($param) = @_;
my($self) = \$colors{$param};
return(bless($self, $class));
}
package main;
print("Executing main statements\n");
$pen = Pen->new(
"PART_NUM" => "12A-34",
"QTY_ON_HAND" => 34,
"INK_COLOR" => "blue");
%properties = %{$pen};
print("The part number is " . $properties{'PART_NUM'} . "\n");
print("The quantity is " . $properties{'QTY_ON_HAND'} . "\n");
print("The ink color is " . ${$properties{'INK_COLOR'}} . "\n");
</PRE>
</BLOCKQUOTE>
<HR>
<P>
This program displays
<BLOCKQUOTE>
<PRE>
Executing Color statements
Executing main statements
The part number is 12A-34
The quantity is 34
The ink color is Die Lot 13
</PRE>
</BLOCKQUOTE>
<P>
Where to start? You already know about the <TT>Inventory_item</TT>
class and the <TT>@ISA</TT> array.
Let's look at the assignment to the <TT>INK_COLOR</TT>
entry of the <TT>Pen</TT> class. This
line, <TT>$self->{"INK_COLOR"}
= Color->new($params{"INK_COLOR"});</TT>,
is used to call the constructor for the <TT>Color</TT>
class. The expression <TT>$params{"INK_COLOR"}</TT>
passes the value of <TT>"blue"</TT>
to the <TT>Color</TT> constructor,
which returns a refereNCe to one of the colors in the <TT>%colors</TT>
associative array.
<P>
You can tell that Perl executes all statements that are not inside
fuNCtions because the <TT>print</TT>
statement in the <TT>Color</TT> package
is executed before the <TT>print</TT>
statement in the <TT>main</TT> package.
This is why you can define hash entries inside the <TT>Color</TT>
class. When variables are defined inside a package but outside
a fuNCtion, they are called <I>static</I> variables. You can access
one of the hash entries in the <TT>Color</TT>
package like this: <TT>$Color::colors{"blue"}</TT>.
<H2><A NAME="StaticVersusRegularMethodsandVariables"><FONT SIZE=5 COLOR=#FF0000>
Static Versus Regular Methods and Variables</FONT></A></H2>
<P>
You already learned that a static method is one that can be called
without needing an instantiated object. Actually, you can also
have static variables as you saw in the last section. Static variables
can be used to emulate <I>constants</I>, values that don't change.
Constants are very useful. For example, you can use them for tax
rates, mathematical constants, and things such as state abbreviations.
Here is an example using a small Perl script:
<BLOCKQUOTE>
<PRE>
package Math;
$math{'PI'} = 3.1415;
package main;
print("The value of PI is $Math::math{'PI'}.\n");
</PRE>
</BLOCKQUOTE>
<P>
This program displays
<BLOCKQUOTE>
<PRE>
The value of PI is 3.1415.
</PRE>
</BLOCKQUOTE>
<P>
You can also do this:
<BLOCKQUOTE>
<PRE>
package Math;
$PI = 3.1415;
package main;
print("The value of PI is $Math::PI.\n");
</PRE>
</BLOCKQUOTE>
<P>
Because you have been using a static method all along-the <TT>new()</TT>
method-I'll take this opportunity to demonstrate a regular fuNCtion.
Listing 14.6 shows how to use the <TT>UNIVERSAL</TT>
package to define a utility fuNCtion that is available to all
classes.
<P>
<IMG SRC="pseudo.gif" tppabs="http://cheminf.nankai.edu.cn/~eb~/Perl%205%20By%20Example/pseudo.gif" BORDER=1 ALIGN=RIGHT><p>
<BLOCKQUOTE>
<I>Start a definition of the </I><TT><I>UNIVERSAL</I></TT><I>
class.<BR>
Define the </I><TT><I>lookup()</I></TT><I>
method.<BR>
DerefereNCe the object refereNCe (the first element of </I><TT><I>@_</I></TT><I>)
and use the second parameter as the key into the anonymous hash.
Return the value of the hash entry.<BR>
Start a definition of the </I><TT><I>Inventory_item</I></TT><I>
class.<BR>
Define the constructor for the class.<BR>
Assign the rest of the parameters to the </I><TT><I>%params</I></TT><I>
hash.<BR>
Bless the anonymous hash with the class name.<BR>
Use </I><TT><I>%params</I></TT><I>
to initialize the class properties.<BR>
Start the </I><TT><I>main</I></TT><I>
namespace.<BR>
Call the constructor for the </I><TT><I>Inventory_item</I></TT><I>
class. Assign the object refereNCe to </I><TT><I>$item</I></TT><I>.
<BR>
Print the two property values using the </I><TT><I>lookup()</I></TT><I>
method to verify that the property initialization worked.</I>
</BLOCKQUOTE>
<HR>
<BLOCKQUOTE>
<B>Listing 14.6 14LST06.PL-Using a Static Method to
Retrieve Class Properties<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<PRE>
package UNIVERSAL;
sub lookup {
return(%{$_[0]}->{$_[1]});
}
package Inventory_item;
sub new {
my($class) = shift;
my(%params) = @_;
my($self) = { };
$self->{"PART_NUM"} = $params{"PART_NUM"};
$self->{"QTY_ON_HAND"} = $params{"QTY_ON_HAND"};
return(bless($self, $class));
}
package main;
$item = Inventory_item->new("PART_NUM"=>"12A-34", "QTY_ON_HAND"=>34);
print("The part number is " . $item->lookup('PART_NUM') . "\n");
print("The quantity is " . $item->lookup('QTY_ON_HAND') . "\n");
</PRE>
</BLOCKQUOTE>
<HR>
<P>
I don't think this example needs any further explanation, so let's
use the space normally reserved to further discussion of the listing
and show you another utility fuNCtion instead. The <TT>printAll()</TT>
fuNCtion shown here displays all the properties of a class, or
you can specify one or more properties to display:
<BLOCKQUOTE>
<PRE>
sub printAll {
my($self) = shift;
my(@keys) = @_ ? @_ : sort(keys(%{$self}));
print("CLASS: $self\n");
foreach $key (@keys) {
printf("\t%10.10s => $self->{$key}\n", $key);
}
}
</PRE>
</BLOCKQUOTE>
<P>
If you put this fuNCtion into the <TT>UNIVERSAL</TT>
package, it will be available to an
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -