📄 ch5.htm
字号:
<TT><FONT FACE="Courier"> Calling with no parameters:
<BR>
<BR>
==================================<BR>
Making a cup<BR>
==================================<BR>
<BR>
Calling with
one parameter:<BR>
<BR>
==================================<BR>
Making a cup<BR>
Add cream<BR>
==================================<BR>
<BR>
Calling with two parameters:<BR>
<BR>
==================================<BR>
Making a cup<BR>
Add cream<BR>
Add 2 sugar cubes<BR>
==================================<BR>
<BR>
Calling with three parameters:<BR>
<BR>
==================================<BR>
Making a cup<BR>
Add cream<BR>
Add 3 sugar cubes<BR>
Making some really addictive coffee ;-)<BR>
==================================</FONT></TT>
</BLOCKQUOTE>
<P>
In any event, you can have default values in the function to set
if the expected parameter is not passed in. Thus, the behavior
of the method can be different depending on the number of arguments
you pass into it.
<H2><A NAME="OverridingMethods"><FONT SIZE=5 COLOR=#FF0000>Overriding
Methods</FONT></A></H2>
<P>
Inheriting functionality from another class is beneficial in that
you can get all the exported functionality of the base class in
your new class. To see an example of how this works, let's add
a function called <TT><FONT FACE="Courier">printType</FONT></TT>
in the <TT><FONT FACE="Courier">Bean.pm</FONT></TT> class. Here's
the subroutine:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">sub printType {<BR>
my $class = shift @_;<BR>
print "The type of Bean is $class->{'Bean'}
\n";<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
Do not forget to update the <TT><FONT FACE="Courier">@EXPORT</FONT></TT>
array by adding the name of the function to export. The new statement
should look like this:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">@EXPORT = qw(setBeanType, printType,
printType);</FONT></TT>
</BLOCKQUOTE>
<P>
Next, call the <TT><FONT FACE="Courier">printType</FONT></TT>
function. The following three lines show three ways to call this
function:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$cup->Coffee::printType();<BR>
$cup->printType();<BR>
$cup->Bean::printType();</FONT></TT>
</BLOCKQUOTE>
<P>
The output from all three lines is the same:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">The type of Bean is Mixed<BR>
The type of Bean is Mixed<BR>
The type of Bean is Mixed</FONT></TT>
</BLOCKQUOTE>
<P>
Why is this so? Because there is no <TT><FONT FACE="Courier">printType()</FONT></TT>
function in the inheriting class, the <TT><FONT FACE="Courier">printType()</FONT></TT>
function in the base class is used instead. Naturally, if you
want your own class to have its own <TT><FONT FACE="Courier">printType</FONT></TT>
function, you would define its own <TT><FONT FACE="Courier">printType</FONT></TT>
function.
<P>
In the <TT><FONT FACE="Courier">Coffee.pm</FONT></TT> file, you
would add the following lines to the end of the file:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">#<BR>
# This routine prints the type of $class->{'Coffee'}<BR>
#<BR>
sub printType {<BR>
my $class = shift @_;<BR>
print "The type of Coffee is $class->{'Coffee'}
\n";<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
<TT><FONT FACE="Courier">@EXPORT</FONT></TT> would also have to
be modified to work with this function:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">@EXPORT = qw(setImports, declareMain,
closeMain, printType);</FONT></TT>
</BLOCKQUOTE>
<P>
The output from the three lines now looks like this:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">The type of Coffee is Instant<BR>
The type of Coffee is Instant<BR>
The type of Bean is Mixed</FONT></TT>
</BLOCKQUOTE>
<P>
Now the base class function is called only when the <TT><FONT FACE="Courier">Bean::</FONT></TT>
override is given. In the other cases, only the inherited class
function is called.
<P>
What if you do not know what the base class name is or even where
the name is defined. In this case, you can use the <TT><FONT FACE="Courier">SUPER::</FONT></TT>
pseudoclass reserved word. Using the <TT><FONT FACE="Courier">SUPER::</FONT></TT>
override allows you to call an overridden superclass method without
actually knowing where that method is defined. The <TT><FONT FACE="Courier">SUPER::</FONT></TT>
construct is meaningful only within the class.
<P>
If you're trying to control where the method search begins and
you're executing in the class itself, you can use the <TT><FONT FACE="Courier">SUPER::</FONT></TT>
pseudoclass, which says to start looking in your base class's
<TT><FONT FACE="Courier">@ISA</FONT></TT> list without having
to explicitly name it.
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$this->SUPER::function( ... argument
list ... );</FONT></TT>
</BLOCKQUOTE>
<P>
Therefore, instead of <TT><FONT FACE="Courier">Bean::</FONT></TT>,
you can use <TT><FONT FACE="Courier">SUPER::</FONT></TT>. The
call to the function <TT><FONT FACE="Courier">printType()</FONT></TT>
becomes this:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$cup->SUPER::printType();</FONT></TT>
</BLOCKQUOTE>
<P>
Here's the output:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">The type of Bean is Mixed</FONT></TT>
</BLOCKQUOTE>
<H2><A NAME="AFewCommentsAboutClassesandObjects"><FONT SIZE=5 COLOR=#FF0000>A
Few Comments About Classes and Objects in Perl</FONT></A></H2>
<P>
One advertised strength of object-oriented languages is the ease
with which new code can use old code. Packages and modules in
Perl provide a great deal of data encapsulation. You are never
really guaranteeing that a class inheriting your code will not
attempt to access your class variables directly. They can if they
really want to. However, this type of procedure is considered
bad practice, and shame on you if you do it.
<P>
When writing a package, you should ensure that everything a method
needs is available via the object or is passed as a parameter
to the method. From within the package, access any global variables
only through references passed in via methods.
<P>
For static or global data to be used by the methods, you have
to define the context of the data in the base class using the
<TT><FONT FACE="Courier">local()</FONT></TT> construct. The subclass
will then call the base class to get the data for it. On occasion,
a subclass may want to override that data and replace it with
new data. When this happens, the superclass may not know how to
find the new copy of the data. In such cases, it's best to define
a reference to the data and then have all base classes and subclasses
modify the variable via that reference.
<P>
Finally, you'll see references to objects and classes like this:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">use Coffee::Bean;</FONT></TT>
</BLOCKQUOTE>
<P>
This code is interpreted to mean "look for <TT><FONT FACE="Courier">Bean.pm</FONT></TT>
in the <TT><FONT FACE="Courier">Coffee</FONT></TT> subdirectory
in all the directories in the <TT><FONT FACE="Courier">@Inc</FONT></TT>
array." So, if you were to move <TT><FONT FACE="Courier">Bean.pm</FONT></TT>
into the <TT><FONT FACE="Courier">./Coffee</FONT></TT> directory,
all the previous examples would work with the new <TT><FONT FACE="Courier">use</FONT></TT>
statement. The advantage to this approach is that you have one
file for the parent class in one directory and the files for each
base class in their own sub-directories. It helps keep code organized.
Therefore, to have a statement like this:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">use Another::Sub::Menu;</FONT></TT>
</BLOCKQUOTE>
<P>
you would see a directory subtree like this:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">./Another/Sub/Menu.pm</FONT></TT>
</BLOCKQUOTE>
<P>
Let's look at an example of a simple portfolio manager class called
<TT><FONT FACE="Courier">Invest.pm</FONT></TT>. There are two
subclasses derived from it that manage the type of funds. The
three files are shown in Listings 5.17, 5.18, and 5.19. The test
code to use these modules is shown in Listing 5.20. The <TT><FONT FACE="Courier">Invest.pm</FONT></TT>
file is placed in the current directory, and the <TT><FONT FACE="Courier">Stock.pm</FONT></TT>
and <TT><FONT FACE="Courier">Fund.pm</FONT></TT> files are placed
in the <TT><FONT FACE="Courier">Invest</FONT></TT> subdirectory.
<HR>
<BLOCKQUOTE>
<B>Listing 5.17. The </B><TT><B><FONT FACE="Courier">./Invest.pm</FONT></B></TT><B>
file.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier"> 1 package Invest;<BR>
2 <BR>
3 require Exporter;
<BR>
4 @ISA = (Exporter);<BR>
5 <BR>
6 =head1 NAME<BR>
7 <BR>
8 Letter - Sample module to simulate Bond behaviour<BR>
9 <BR>
10 =head1 SYNOPSIS<BR>
11 <BR>
12 use Invest;<BR>
13 use Invest::Fund;<BR>
14 use Invest::Stock;<BR>
15 <BR>
16 $port = new Invest::new();<BR>
17 <BR>
18 $i1 = Invest::Fund('symbol'
=> 'twcux');<BR>
19 $i2 = Invest::Stock('symbol'
=> 'INTC');<BR>
20 $i3 = Invest::Stock('symbol'
=> 'MSFT');<BR>
21 <BR>
22 $port->Invest::AddItem($i1);
<BR>
23 $port->Invest::AddItem($i2);
<BR>
24 $port->Invest::AddItem($i3);
<BR>
25 <BR>
26 $port->ShowPortfolio();
<BR>
27 <BR>
28 =head1 DESCRIPTION<BR>
29 <BR>
30 This module provides a short example of generating a letter
for a<BR>
31 friendly neighborbood loan shark.<BR>
32 <BR>
33 The code begins after the "cut" statement.<BR>
34 =cut<BR>
35 <BR>
36 @EXPORT = qw( new, AddItem, ShowPortfolio, PrintMe);<BR>
37 <BR>
38 @portfolio = ();<BR>
39 $portIndex = 0;<BR>
40 <BR>
41 sub Invest::new {<BR>
42 my $this
= shift;<BR>
43 my $class
= ref($this) || $this;<BR>
44 my $self
= {};<BR>
45 bless
$self, $class;<BR>
46 $portIndex = 0;<BR>
47 <BR>
48 printf "\n Start portfolio";
<BR>
49 return
$self;<BR>
50 }<BR>
51 <BR>
52 sub Invest::AddItem {<BR>
53 my ($type,$stock) = @_;
<BR>
54 $portfolio[$portIndex] =
$stock;<BR>
55 $portIndex++;<BR>
56 }<BR>
57 <BR>
58 sub Invest::ShowPortfolio {<BR>
59 my $i;<BR>
60 printf "\n Our Portfolio
is:";<BR>
61 foreach $i (@portfolio)
{<BR>
62
print "\n ". $i->{'shares'} . " shares
of " . $i->{'symbol'};<BR>
63 }<BR>
64 print "\n";<BR>
65 }<BR>
66 <BR>
67 sub PrintMe {<BR>
68 my $this = shift;<BR>
69 print "\n Class : $$this";
<BR>
70
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -