📄 ch14.htm
字号:
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, </I><TT><I>Inventory_item</I></TT><I>,
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.<BR>
Bless the anonymous hash so that </I><TT><I>ref()</I></TT><I>
will return </I><TT><I>Pen</I></TT><I>
and return a refereNCe to the anonymous hash.<BR>
Start the </I><TT><I>main</I></TT><I>
namespace.<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>.
Note that an array with property-value pairs is passed to the
constructor.<BR>
Print the three property values to verify that the property initialization
worked.</I>
</BLOCKQUOTE>
<HR>
<BLOCKQUOTE>
<B>Listing 14.3 14LST03.PL-How to Call the Constructor
of a Parent 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"} = $params{"INK_COLOR"};
return(bless($self, $class));
}
package main;
$pen = Pen->new(
"PART_NUM" => "12A-34",
"QTY_ON_HAND" => 34,
"INK_COLOR" => "blue");
print("The part number is " . %{$pen}->{'PART_NUM'} . "\n");
print("The quantity is " . %{$pen}->{'QTY_ON_HAND'} . "\n");
print("The ink color is " . %{$pen}->{'INK_COLOR'} . "\n");
</PRE>
</BLOCKQUOTE>
<HR>
<P>
This program displays:
<BLOCKQUOTE>
<PRE>
The part number is 12A-34
The quantity is 34
The ink color is blue
</PRE>
</BLOCKQUOTE>
<P>
You should be familiar with all the aspects of this script by
now. The line <TT>my($self) = Inventory_item->new(@_);</TT>
is used to get a refereNCe to an anonymous hash. This hash becomes
the object for the base class.
<P>
To understand that calling the parent constructor creates the
object that becomes the object for the base class, you must remember
that an object <I>is</I> the anonymous hash. Because the parent
constructor creates the anonymous hash, the base class needs a
refereNCe only to that hash in order to add its own properties.
This refereNCe is stored in the <TT>$self</TT>
variable.
<P>
You may also see the variable name <TT>$this</TT>
used to hold the refereNCe in some scripts. Both <TT>$self</TT>
and <TT>$this</TT> are acceptable
in the object-oriented world.<BR>
<p>
<CENTER>
<TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR><TD><B>Note</B></TD></TR>
<TR><TD>
<BLOCKQUOTE>
I would actually prefer the variable name <TT>$data</TT> because the hash <TT><I>is</I></TT> the object; therefore, the data <TT><I>is</I></TT> the object. But sometimes, it's good to follow conventional wisdom so that others can more easily understand
your programs.
</BLOCKQUOTE>
</TD></TR>
</TABLE>
</CENTER>
<P>
<H3><A NAME="ExamplePolymorphism">
Example: Polymorphism</A></H3>
<P>
<I>Polymorphism</I>, although a big word, is a simple coNCept.
It means that methods defined in the base class will override
methods defined in the parent classes. The following small example
clarifies this coNCept:
<BLOCKQUOTE>
<PRE>
package A;
sub foo {
print("Inside A::foo\n");
}
package B;
@ISA = (A);
sub foo {
print("Inside B::foo\n");
}
package main;
B->foo();
</PRE>
</BLOCKQUOTE>
<P>
This program displays
<BLOCKQUOTE>
<PRE>
Inside B::foo
</PRE>
</BLOCKQUOTE>
<P>
The <TT>foo()</TT> defined in class
<TT>B</TT> overrides the definition
that was inherited from class <TT>A</TT>.
<P>
Polymorphism is mainly used to add or extend the fuNCtionality
of an existing class without reprogramming the whole class. Listing
14.4 uses polymorphism to override the <TT>qtyChange()</TT>
fuNCtion inherited from <TT>Inventory_item</TT>.
In addition, it shows how to call a method in a parent class when
the specific parent class name (also known as the <TT><I>SUPER</I></TT>
class) is unknown.
<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>
Define the </I><TT><I>qtyChange()</I></TT><I>
method.<BR>
Get the object refereNCe from the parameter array.<BR>
Get the quantity to change from the parameter array. If there
are no more elements in the </I><TT><I>@_</I></TT><I>,
default to using the quantity 1.<BR>
Use derefereNCing to change the </I><TT><I>QTY_ON_HAND</I></TT><I>
property.<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>
Initialize the </I><TT><I>@PARENT::ISA</I></TT><I>
array to let Perl search the </I><TT><I>@ISA</I></TT><I>
to look for method refereNCes.<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 using the </I><TT><I>PARENT::</I></TT><I>
notation. This searches the classes listed in the </I><TT><I>@ISA</I></TT><I>
array looking for the </I><TT><I>new()</I></TT><I>
fuNCtion and assigns 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.<BR>
Return a refereNCe to the anonymous hash.<BR>
Define the </I><TT><I>qtyChange()</I></TT><I>
method.<BR>
Get the object refereNCe from the parameter array.<BR>
Get the quantity to change from the parameter array. If there
are no more elements in the </I><TT><I>@_</I></TT><I>,
default to using the quantity 100.<BR>
Use derefereNCing to change the </I><TT><I>QTY_ON_HAND</I></TT><I>
property.<BR>
Start the </I><TT><I>main</I></TT><I>
namespace.<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>
Print the data type of </I><TT><I>$item</I></TT><I>
to show that it is now Pen.<BR>
Print the three property values to verify that the property initialization
worked.<BR>
Change the quantity by the default amount.<BR>
Print a newline to separate the previous values from the new value.
<BR>
Print the quantity property value to verify that the change method
worked.</I>
</BLOCKQUOTE>
<HR>
<BLOCKQUOTE>
<B>Listing 14.4 14LST04.PL-Accessing Methods in Parent
Classes<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;
}
sub qtyChange {
my($self) = shift;
my($delta) = $_[0] ? $_[0] : 1;
$self->{"QTY_ON_HAND"} += $delta;
}
package Pen;
@ISA = ("Inventory_item");
@PARENT::ISA = @ISA;
sub new {
my($class) = shift;
my(%params) = @_;
my($self) = $class->PARENT::new(@_);
$self->{"INK_COLOR"} = $params{"INK_COLOR"};
return($self);
}
sub qtyChange {
my($self) = shift;
my($delta) = $_[0] ? $_[0] : 100;
$self->PARENT::qtyChange($delta);
}
package main;
$pen = Pen->new(
"PART_NUM"=>"12A-34",
"QTY_ON_HAND"=>340,
"INK_COLOR" => "blue");
print("The data type is " . ref($pen) . "\n");
print("The part number is " . %{$pen}->{'PART_NUM'} . "\n");
print("The quantity is " . %{$pen}->{'QTY_ON_HAND'} . "\n");
print("The ink color is " . %{$pen}->{'INK_COLOR'} . "\n");
$pen->qtyChange();
print("\n");
print("The quantity is " . %{$pen}->{'QTY_ON_HAND'} . "\n");
</PRE>
</BLOCKQUOTE>
<HR>
<P>
This program displays
<BLOCKQUOTE>
<PRE>
The data type is Pen
The part number is 12A-34
The quantity is 340
The ink color is blue
The quantity is 440
</PRE>
</BLOCKQUOTE>
<P>
The first interesting line in the preceding example is <TT>my($delta)
= $_[0] ? $_[0] : 1;</TT>. This line checks to see if
a parameter was passed to <TT>Inventory_item::qtychange()</TT>
and if not, assigns a value of 1 to <TT>$delta</TT>.
This line of code uses the <TT>ternary</TT>
operator to determine if <TT>$_[0]</TT>
has a value or not. A zero is used as the subscript because the
class refereNCe was shifted out of the parameter array and into
<TT>$self</TT>.
<P>
The next interesting line is <TT>@PARENT::ISA
= @ISA;</TT>. This assignment lets you refer to a method
defined in the parent class. Perl searches the parent hierarchy
(the <TT>@ISA</TT> array) until a
definition is found for the requested fuNCtion.
<P>
The <TT>Pen::new()</TT> fuNCtion uses
the <TT>@PARENT::ISA</TT> to find
the parent constructor using this line: <TT>my($self)
= $class->PARENT::new(@_);</TT>. I don't really recommend
calling parent constructors in this manner because the constructor
that gets called will depend on the order of classes in the <TT>@ISA</TT>
array. Having code that is dependent on an array keeping a specific
order is a recipe for disaster; you might forget about the dependeNCy
and spend hours trying to find the problem. However, I thought
you should see how it works. Because the <TT>$class</TT>
variable (which is equal to <TT>Pen</TT>)
is used to locate the parent constructor, the hash will be blessed
with the name of the base <TT>Pen</TT>
class-one small advantage of this technique. This is shown by
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -