Today's lesson teaches you how to use the object-oriented
programming (OOP) features of Perl as well as how to construct objects
in Perl. The discussion also includes inheritance, overriding methods,
and data encapsulation.
A module is a Perl package. Objects in Perl are based
on references to data items within a package. An object in
Perl is simply a reference to something that knows which class it
belongs to. (References were covered on Day 18, "References
in Perl 5.") For more information, you can consult the perlmod
and perlobj text files at http://www.metronet.com.
These files are the primary source of information on the Internet
about Perl modules.
In object-oriented programming with other languages, you
declare a class and then create objects of that class. All
objects of a particular class behave in a certain way, which is
governed by the methods of that class. You can create new classes
by defining new ones or by inheriting properties from an existing
class.
Programmers already familiar with object-oriented principles
will recognize the terminology used here.. Perl is, and pretty much
always has been, an object-oriented language. In Perl 4, the use
of packages provided different symbol tables from which to choose
symbol names. Perl 5 changes the syntax a bit and somewhat
formalizes the use of objects .
The next three declarations are extremely important to
understanding how objects, classes, and methods work in Perl.
The rest of today's lesson covers each of the preceding items
in more detail.
One rule is important enough to repeat: A Perl class is simply
a package. When you see a Perl document that refers to a "class,"
think "package." Existing Perl 5 syntax enables you to
create a class. If you are already a C programmer, you do not have
to know a lot of new syntax. What might be a new concept to Perl
4 programmers is the use of the double colon (::) to signify
the base and inherited classes.
One of the key features of OOP is inheritance. The inheritance
feature offered by Perl, however, is not the same as you might expect
from other object-oriented languages. Perl classes inherit methods only;
you must use your own mechanisms to implement data inheritance.
Because each class is a package, it has its own name space
with its own associative array of symbol names. Each class can therefore
use its own independent set of symbol names. As with package references,
you can address the variables in a class with the back quote (')
operator. Members of a class are addressed as $class'$member.
In Perl 5, you can use the double colon instead of the '
to get the reference. For example, $class'member
is the same as $class::$member.
This section covers the requisite steps to take when you
create a new class. The example will illustrate the semantics in
the creation of a simple class called Cocoa, which is used
for printing the required parts of a source code file for a
simple Java application. You will not become a Java expert, nor
will this package require you to have any experience in Java; the
focus is the concept of creating a class. The example could have
just as easily used a phone book application, but how many
similar examples have you already seen in books?
Note: I am currently still developing the package Java.pm. It's named Cocoa.pm in development because it does not have the high caffeine content of a full-featured, or even mildly useful, Java.pm package. Perhaps after reading today's lesson you will be able to contribute to the Java.pm Perl package; if so, send e-mail to khusain@ikra.com.
Time now for a shameless plug for Perl Unleashed, which is also by Sams Publishing, due the summer of 1996. It will contain gobs of information about writing and using classes and packages--and track the initial development stages of the Java.pm package. (Hmmm. Maybe the package should be called Bean.pm in its early stages.)
First of all, create a package file called Cocoa.pm.
(The .pm extension, which is the default extension for
packages, stands for Perl module.) A module is a package, and a
package is a class for all practical purposes. Before you do
anything else, place a 1; in the file. As you add more lines to
the package file, make sure you keep the 1; as the last
line. The following code shows the basic structure of the file:
package Cocoa; # # Put "require" statements in for all required,imported packages # # # Just add code here # 1; # terminate the package with the required 1;
This requirement is important: Don't forget to always keep the 1;
line as the last of the package file. This statement is required for
all packages in Perl. If you forget this statement, your package will
not be processed by Perl.
Congratulations; you have just created your first package
file. Now you are ready to add your methods to this package and make
it a class. The first method you should add is the new() method, which
must be called whenever you create a new object. The new()
method is the constructor for the object.
A constructor is a Perl subroutine in a class that
returns a reference to something that has the class name attached
to it. Connecting a class name with a reference is referred to as
"blessing" an object because the function to establish
the connection is called bless.
The following code segment shows the syntax for the bless
function:
bless YeReference [,classname]
YeReference is the reference to the object being
blessed. The classname is optional and specifies the
name of the package from which the object will get methods. If
the classname is not specified, the name of the
current package is used instead.
The way to create a constructor in Perl is to return a
reference to an internal structure that has been blessed into
this Cocoa class. Listing 19.1 shows the initial Cocoa.pm
package.
Listing 19.1. The initial Cocoa.pm
package.
package Cocoa;
sub new {
my $this = {}; # Create an anonymous hash, and #self points to it.
bless $this; # Connect the hash to the package Cocoa.
return $this; # Return the reference to the hash.
}
1;
There is no output for Listing 19.1.
The {} constructs a reference to a hash that contains
no key/value pairs. The returned value to this hash is assigned
to the local variable $this. The bless() function
takes that reference to $this, tells the object it
references that it's now a Cocoa, and returns the
reference.
The returned value to the calling function now refers to this
anonymous hash. On return from the new() function, the $this reference
is destroyed, but the calling function keeps a reference to this hash. Therefore,
the reference count to the hash won't be zero and Perl keeps the
hash in memory. (You do not have to keep it around, but it's nice
to have it around for reference later.)
To create an object, you make a call such as the following:
$cup = new Cocoa;
Listing 19.2 shows you how to use this package to create the
constructor.
Listing 19.2. ????
???Kamran: please add an appropriate descriptive heading for the listing. Thanks--Tonya
1 #!/usr/bin/perl
2 push (@INC,`pwd`);
3 use Cocoa;
4 $cup = new Cocoa;
Line 1 refers to the location of the Perl interpreter to use.
Your Perl interpreter may be located at /usr/local/bin/perl
or wherever you installed it.
In line 2, the local directory is added to the search path in @INC
for the list of paths to use when looking for a package. You can
create your module in a different directory and specify the path explicitly
there. Had I created the package in /home/khusain/test/scripts/,
line 2 would read as follows:
push (@INC,"/home/khusain/test/scripts");
In line 3, you include the package Cocoa.pm to get all
the functionality in your script. The use statement asks
Perl to look in the @INC path for a file called Cocoa.pm
and include it in the copy of the source file being parsed. The use
statement is required if you want to work with a class.
Line 4 creates the Cocoa object by calling the new
function on it. Now comes the beauty (and confusion and power) of
Perl. There is more than one way to do this. You can rewrite line
3 as the following:
???Author: Last sentence: Do you mean Line 4? --Kris
???Kamran: third sentence, there is more than one way to do
what, exactly? PLease specify what "this" refers to.
Thanks--Tonya
$cup = Cocoa->new();
If you are a C programmer, you can use the double colons (::)
to force the function new() from the Cocoa package.
As a result, line 4 could also be written as the following:
$cup = Cocoa::new();
Nothing prevents you from adding more code in the constructor
than what is shown here. For the Cocoa.pm module, you can,
if you like, print a disclaimer when each object is created. You
might want to use the constructor to initialize variables or set
up arrays or pointers specific to the module.
DO initialize variables in your module in the constructor.
DO use the my construct to create variables in a method.
DON'T use the local construct in a method unless you really do want the variables to be passed down to other subroutines.
DON'T use global variables in the class module.
Tip: When you are working with instance variables, it is sometimes easy to visualize a Perl object as simply an associative array. Then its easy to see that each index in the associative array is a member of that class and each item at the index of the associative array is a value of that member.
Listing 19.3 shows what the Cocoa constructor looks
like.
Listing 19.3. Revised Constructor for
Cocoa.pm.
sub new {
my $this = {};
print "\n /* \n ** Created by Cocoa.pm \n ** Use at own risk";
print "\n ** Did this code even get pass the javac compiler? ";
print "\n **/ \n";
bless $this;
return $this;
}
The following shows the output from running the test script
called testme on this bare-bones class:
$ testme /* ** Created by Cocoa.pm ** Use at own risk ** Did this code even get pass the javac compiler? **/
Regardless of which of the three methods shown here you used
to create the Cocoa object, you should see the same
output.
Great. Now you've created some comments at the beginning of a
file with some print statements. You can just as easily call other
functions in or outside of the package to get more initialization
functionality. For example, as development progresses, you see
the new() function evolve to resemble the following:
sub new {
my $this = {}
bless $this;
$this->doInitialization();
return $this;
}
When you create any given class, you should allow it to be
inherited. You should be able to call the new operator
with the class name as the first parameter. This capability to
parse the class name from the first argument causes the class to
be inherited. As a result, the new function becomes more or less
like the following:
sub new {
my $class = shift; # Get the request class name
my $this = {};
bless $this, $class # Use class name to bless() reference
$this->doInitialization();
return $this;
}
The preceding method forces your class users to make calls in
the form of one of three ways:
What if you wanted to use a reference to the object instead,
such as $obj->new()? The doInitialization()
method used will be whatever $class you blessed the object
into. The following code uses the function call ref() to
determine if the class exists per se. The ref() function
returns true if the item passed to it is a reference and null
if it is not a reference. With classes, the true value returned
from the ref() function is the name of the class.
sub new {
my $this = shift; # Get the class name
my $class = ref($this) || $this;
[ic:ccc]# If class exists, use it else use reference.
my $this = {};
bless $this, $class
$this->doInitialization();
return $this;
}
Within the class package, the methods typically treat the
reference as an ordinary reference. Outside the class package,
the reference is generally treated as an opaque value that can
only be accessed through the class's methods. You can access the values
within the package directly, but it's not a good idea to do so
because such access defeats the whole purpose of object orientation.
It's possible to bless a reference object more than once.
However, the caveat is that the new class must get rid of the
object at the previously blessed reference. For C and Pascal
programmers, this is like assigning a pointer to malloced
memory and then assigning the same pointer to another location without
first freeing the previous location. In effect, a Perl object
must belong to only one class at a time.
What's the real difference between an object and a reference?
Perl objects are blessed to belong to a class. References are not
blessed; if they are, they belong to a class and are objects.
Objects know to which class they belong. References do not have a
class to which they belong.
The arguments to a new() function for a constructor are
called instance variables. Instance variables are used to
do initialization for each instance of an object as it's created.
For example, the new() function could expect a name for
each new instance of object created. Using instance variables
allows you to customize each object as it is created.
???Kamran: second to last sentence, is there a word missing in "instance of object created"?--Tonya
You can use either an anonymous array or anonymous hash to
hold instance variables. To use a hash to store the parameters coming
in, the code would resemble the following:
sub new {
my $type = shift;
my %parm = @_;
my $this = {};
$this->{'Name'} = $parm{'Name'};
$this->{'x'} = $parm{'x'};
$this->{'y'} = $parm{'y'};
bless $this, $type;
}
You can also use an array instead of a hash to store the
instance variables.
sub new {
my $type = shift;
my %parm = @_;
my $this = [];
$this->[0] = $parm{'Name'};
$this->[1] = $parm{'x'};
$this->[2] = $parm{'y'};
bless $this, $type;
}
To construct an object, you can pass the parameters with the new()
function call. For example, the call to create the Cocoa object
becomes the following:
$mug = Cocoa::new( 'Name' => 'top', 'x' => 10, 'y' => 20 );
The => operator has the same function of the comma
operator, but => is a bit more readable. You can write
this code with commas instead of the => operator if you
prefer.
To access the variables as you would any other data members,
you can use the following statements:
print "Name=$mug->{'Name'}\n";
print "x=$mug->{'x'}\n";
print "y=$mug->{'y'}\n";
A method in a Perl class is simply a Perl subroutine.
Perl doesn't provide any special syntax for method definition. A
method expects its first argument to be the object or package on
which it is invoked. Perl has two types of methods: static and
virtual.
A static method expects a class name as the first
argument. A virtual method expects a reference to an
object as the first argument. The way each method handles the
first argument determines whether the method is static or
virtual.
A static method applies functionality to the entire class as a
whole because it uses the name of the class. Functionality in
static methods is therefore applicable to all objects of the
class. Generally, static methods ignore the first argument
because they already know which class they are in. Constructors
are static methods.
A virtual method expects a reference to an object as its first
argument. Typically, the first thing a virtual method does is
shift the first argument into a self or this
variable and then use that shifted value as an ordinary
reference. For example, consider the following code:
1. sub nameLister {
2. my $this = shift;
3. my ($keys ,$value );
4. while (($key, $value) = each (%$this)) {
5. print "\t$key is $value.\n";
6. }
7. }
Line 2 in the listing is where the $this variable is
set to point to the object. In line 4, the $this array is de-referenced
at every $key location.
** BEGIN TIP
Tip: Look at the .pm files in the Perl distribution for sample code that will show you how methods are declared and used.
If you tried to invoke the Cocoa.pm package right now,
you'd get an error message from Perl at compile time about the methods
not being found. This error occurs because the Cocoa.pm methods have
not been exported. To export these functions, you need the
Exporter module. Add the following lines to the beginning of code
in the package:
require Exporter; @ISA = qw(Exporter);
These two lines force the inclusion of the Exporter.pm
module and then set the @ISA array with the name of the
Exporter class to look for.
To export your own classs methods, you list them in the @EXPORT
array. For example, to export the closeMain and declareMain
methods, you use the following statement:
@EXPORT(declareMain, closeMain);
Inheritance in a Perl class is through the @ISA array.
The @ISA array does not have to be defined in every
package; however, when it is defined, Perl treats it as a special
array of directory names. This array is similar to the @INC
array, where directories are searched for files to include. The @ISA
array contains the names of the classes (packages) to look for methods
in other classes in if a method in the current package is not
found. The @ISA array contains the names of the base classes from
which the current class inherits. The search is done in the order
that the classes are listed in the @ISA arrays.
All methods called by a class must belong to the same class or
the base classes defined in the @ISA array. If a method
isn't found in the @ISA array, Perl looks for an AUTOLOAD() routine.
This optional routine is defined as sub in the current package.
To use the AUTOLOAD function, you call the autoload.pm
package with the use Autoload; statement. The AUTOLOAD function
tries to load the called function from the installed Perl
libraries. If the AUTOLOAD call also fails, Perl makes one
final try at the UNIVERSAL class, which is the catch-all
for all methods not defined elsewhere. Perl generates an error
about unresolved functions if this step also fails.
There are two ways to invoke a method for an object: by making
a reference to an object (virtual) or explicitly referring to the class
name (static). You have to export a method to be able to call it. Add
a few more methods to the Cocoa class to get the file to
resemble Listing 19.4.
package Cocoa;
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(setImports, declareMain, closeMain);
#
# This routine creates the references for imports in Java functions
#
sub setImports{
my $class = shift @_;
my @names = @_;
foreach (@names) {
print "import " . $_ . ";\n";
}
}
#
# This routine declares the main function in a Java script
#
sub declareMain{
my $class = shift @_;
my ( $name, $extends, $implements) = @_;
print "\n public class $name";
if ($extends) {
print " extends " . $extends;
}
if ($implements) {
print " implements " . $implements;
}
print " { \n";
}
#
# This routine declares the main function in a Java script
#
sub closeMain{
print "} \n";
}
#
# This subroutine creates the header for the file.
#
sub new {
my $this = {};
print "\n /* \n ** Created by Cocoa.pm \n ** Use at own risk \n */ \n";
bless $this;
return $this;
}
1;
Now, write a simple Perl script to use the methods for this
class. Because you can only start and close the header, examine the
following code for a script to create a skeleton Java applet
source:
#!/usr/bin/perl use Cocoa; $cup = new Cocoa; $cup->setImports( 'java.io.InputStream', 'java.net.*'); $cup->declareMain( "Msg" , "java.applet.Applet", "Runnable"); $cup->closeMain();
This script generates code for a Java applet called Msg
that extends the java.applet.Applet applet and implements
functions that are runnable. You call the function with the $cup->...
call. The following three lines of code:
$cup->setImports( 'java.io.InputStream', 'java.net.*');3 $cup->declareMain( "Msg" , "java.applet.Applet", "Runnable"); $cup->closeMain();
could be rewritten as functions:
Cocoa::setImports($cup, 'java.io.InputStream', 'java.net.*'); Cocoa::declareMain($cup, "Msg" , "java.applet.Applet", "Runnable"); Cocoa::closeMain($cup);
This type of equivalence was shown in the section
"Blessing a Constructor," earlier today. In both cases,
the first parameter is the reference to the object itself.
Running the test script shown generates the following output:
/*
** Created by Cocoa.pm
** Use at own risk
*/
import java.io.InputStream;
import java.net.*;
public class Msg extends java.applet.Applet implements Runnable {
}
An important note about calling the methods: If you have any
arguments in a method, use parentheses if you are using the -> (also
known as indirect) method. The parentheses are required to
include all the arguments with the following statement:
$cup->setImports( 'java.io.InputStream', 'java.net.*');
However, the following statement
Cocoa::setImports($cup, 'java.io.InputStream', 'java.net.*');
can also be rewritten without parentheses as this:
Cocoa::setImports $cup, 'java.io.InputStream', 'java.net.*' ;
The choice is yours about how you make your code readable to
other programmers. Use parentheses if you feel that it will make
the code more readable.
Sometimes you want to specify which class's method to use,
such as when the same named method is specified in two different
classes. For example, if the function grind is defined in
both Espresso and Qava classes, you can specify
which class's function to use by using the :: operator. The following
calls would use the call in Espresso:
$mess = Espresso::grind("whole","lotta","bags");
Espresso::grind($mess, "whole","lotta","bags");
The following calls would use the grind() function in
the Qava class:
$mess = Qava::grind("whole","lotta","bags");
Qava::grind($mess, "whole","lotta","bags");
You might want to call a method based on some action that the
program you are writing has already taken. In other words, you
want to use the Qava method for a certain condition and
the Espresso method for another. In this case, you can use symbolic
references to make the call to the required function, as in the
following example:
$method = $local ? "Qava::" : "Espresso::";
$cup->{$method}grind(@args);
Perl tracks the number of links to objects. When the last
reference to an object is freed to the memory pool, the object is automatically
destroyed. This destruction of the object could occur after your
code stops and the script is about to exit. For global variables,
the destruction happens after the last line in your code
executes.
If you want to capture control just before the object is
freed, you can define a DESTROY() method in your class.
Note the use of all capital letters in the name. The DESTROY()
method is called just before the object is released, which
enables you to do any necessary cleanup. The DESTROY() function
does not call other DESTROY() functions automatically;
Perl doesn't do nested destruction for you. If your constructor
re-blessed a reference from one of your base classes, your DESTROY()
might need to call DESTROY() for any base classes. All
object references that are contained in a given object are freed
and destroyed automatically when the current object is freed.
Usually, you do not have to define a DESTROY function,
but when you do need it, it takes the following form:
sub DESTROY {
#
# Add code here.
#
}
For most purposes, Perl uses a simple, reference-based garbage
collection system. The number of references to any given object
at the time of garbage collection must be greater than zero, or
the memory for that object is freed. When your program exits, an
exhaustive search-and-destroy function in Perl does garbage
collection. Everything in the process is summarily deleted. In
UNIX or UNIX-like systems, this might seem like a waste, but it's
actually quite necessary to perform in embedded systems or in a
multithreaded environment.
Methods in classes are inherited with the paths in the @ISA
array. Variables must be set up explicitly for inheritance.
Assume you define a new class called Bean.pm to include
some of the functionality that another class Coffee.pm
will inherit.
The example in this section demonstrates how to inherit
instance variables from one class (also referred to as a
"superclass" or "base class"). The steps in
inheritance require calling the superclass's constructor and
adding one's own instance variables to the new object.
In this example, the Coffee class inherits values from
the base class called Bean. The two files are called Coffee.pm
and Bean.pm, respectively.
Listing 19.5 is the code for Bean.pm.
Listing 19.5. The code for Bean.pm.
package Bean;
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(setBeanType);
sub new {
my $type = shift;
my $this = {};
$this->{'Bean'} = 'Colombian';
bless $this, $type;
return $this;
}
#
# This subroutine sets the class name
sub setBeanType{
my ($class, $name) = @_;
$class->{'Bean'} = $name;
print "Set bean to $name \n";
}
1;
Listing 19.5 has no output.
In this listing, the $this variable sets a value in the
anonymous hash for the 'Bean' type to be 'Colombian'.
The setBeanType() method is also declared so that the 'Bean'
type can also be changed by a program.
The subroutine for resetting the value of 'Bean' uses
the $class reference to get to the anonymous hash for the
object. Remember that a reference to this anonymous hash created
the reference in the first place with the new() function.
The values in the Bean class will be inherited by the Coffee
class. The Coffee.pm file is shown in Listing 19.6.
Listing 19.6. The Coffee.pm file.
1 #
2 # The Coffee.pm file to illustrate inheritance.
3 #
4 package Coffee;
5 require Exporter;
6 require Bean;
7 @ISA = qw(Exporter, Bean);
8 @EXPORT = qw(setImports, declareMain, closeMain);
9 #
10 # set item
11 #
12 sub setCoffeeType{
13 my ($class,$name) = @_;
14 $class->{'Coffee'} = $name;
15 print "Set coffee type to $name \n";
16 }
17 #
18 # constructor
19 #
20 sub new {
21 my $type = shift;
22 my $this = Bean->new(); ##### <--- LOOK HERE!!! ####
23 $this->{'Coffee'} = 'Instant'; # unless told otherwise
24 bless $this, $type;
25 return $this;
26 }
27 1;
Listing 19.6 has no output]
Note the use of the require Bean; statement in line 6.
This line forces the inclusion of the Bean.pm file and all
its related functions. Lines 12 through 16 define a subroutine to
use when resetting the value of the local variable in $class->{'Coffee'}.
Look at the new() constructor for the Coffee
class in line 20. The $this reference points to the anonymous
hash returned by Bean.pm and not a hash created locally.
In other words, the following statement creates an entirely
different hash that has nothing to do with the hash created in
the Bean.pm constructor:
my $this = {}; # This is not the way to do it for inheritance.
my $this = $theSuperClass->new(); # this is the way.
Listing 19.7 shows how to call these functions.
Listing 19.7. Calling inherited
methods.
1 #!/usr/bin/perl
2 push (@INC,`pwd`);
3 use Coffee;
4 $cup = new Coffee;
5 print "\n -------------------- Initial values ------------ \n";
6 print "Coffee: $cup->{'Coffee'} \n";
7 print "Bean: $cup->{'Bean'} \n";
8 print "\n -------------------- Change Bean Type ---------- \n";
9 $cup->setBeanType('Mixed');
10 print "Bean Type is now $cup->{'Bean'} \n";
11 print "\n ------------------- Change Coffee Type ---------- \n";
12 $cup->setCoffeeType('Instant');
13 print "Type of coffee: $cup->{'Coffee'} \n";
-------------------- Initial values ------------
Coffee: Instant
Bean: Colombian
-------------------- Change Bean Type ----------
Set bean to Mixed
Bean Type is now Mixed
------------------- Change Coffee Type ----------
Set coffee type to Instant
Type of coffee: Instant
The initial values for the 'Bean' and 'Coffee'
indices in the anonymous hash for the object are printed first.
The member functions are called to set the values to different
names and then printed.
Methods can have several types of arguments. It's how you
process the arguments that counts. For example, you can add the following
method to the Coffee.pm module:
sub makeCup {
my ($class, $cream, $sugar, $dope) = @_;
print "\n================================== \n";
print "Making a cup \n";
print "Add cream \n" if ($cream);
print "Add $sugar sugar cubes\n" if ($sugar);
print "Making some really addictive coffee ;-) \n" if ($dope);
print "================================== \n";
}
The function makeCup() takes three arguments but only
processes them if it sees them. To test this functionality,
consider Listing 19.8.
Listing 19.8. Using the makeCup()
function.
1 #!/usr/bin/perl
2 push (@INC,`pwd`);
3 use Coffee;
4 $cup = new Coffee;
5 #
6 # With no parameters
7 #
8 print "\n Calling with no parameters: \n";
9 $cup->makeCup;
10 #
11 # With one parameter
12 #
13 print "\n Calling with one parameter: \n";
14 $cup->makeCup('1');
15 #
16 # With two parameters
17 #
18 print "\n Calling with two parameters: \n";
19 $cup->makeCup(1,'2');
20 #
21 # With all three parameters
22 #
23 print "\n Calling with three parameters: \n";
24 $cup->makeCup('1',3,'1');
Calling with no parameters:
==================================
Making a cup
==================================
Calling with one parameter:
==================================
Making a cup
Add cream
==================================
Calling with two parameters:
==================================
Making a cup
Add cream
Add 2 sugar cubes
==================================
Calling with three parameters:
==================================
Making a cup
Add cream
Add 3 sugar cubes
Making some really addictive coffee ;-)
==================================
Line 9 calls the function with no parameters. In line 14, the
function call has one parameter. The parameters are passed either as
strings or integers, something this particular method does not
care about. Look at line 19 and line 24, where both strings and
numbers are passed in the same function call. However, some
methods you write in the future might require this distinction.
In any event, you can have default values set in the function
if the expected parameter is not passed. The behavior of the method
can be different depending on the number of arguments you pass
it.
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, add a function
in the Bean.pm class called printType. Here's the
subroutine:
sub printType {
my $class = shift @_;
print "The type of Bean is $class->{'Bean'} \n";
}
Do not forget to update the @EXPORT array by adding the
name of the function to export. The new statement should look like
this:
@EXPORT = qw(setBeanType, printType, printType);
Now call the printType function. The next three lines
show three ways to call the function:
$cup->Coffee::printType(); $cup->printType(); $cup->Bean::printType();
The output from all three lines is the same:
The type of Bean is Mixed The type of Bean is Mixed The type of Bean is Mixed
Why is this so? There is no printType() function in the
inheriting class, so the printType() function in the base
class is used instead. Naturally, if you want your own class to
have its own printType function, you have to define it.
In the Coffee.pm file, add the following lines:
#
# This routine prints the type of $class->{'Coffee'}
#
sub printType {
my $class = shift @_;
print "The type of Coffee is $class->{'Coffee'} \n";
}
You must also modify the @EXPORT to work with this
function:
@EXPORT = qw(setImports, declareMain, closeMain, printType);
Now the output from the three lines looks like this:
The type of Coffee is Instant The type of Coffee is Instant The type of Bean is Mixed
The base class function is called only when the Bean::
override is given. In the other cases, only the inherited class
function is called.
What if you do not know the base class name or even where the
name is defined? In this case, you can use the SUPER:: pseudo-class
reserved word. Using the SUPER:: override enables you to
call an overridden superclass method without actually knowing
where that method is defined. The SUPER:: construct is
only meaningful within the class.
If you're trying to control where the method search begins and
you're executing in the class itself, you can use the SUPER:: pseudo
class, which instructs Perl to start looking in your base class's @ISA
list without explicitly naming it:
$this->SUPER::function( ... argument list ... );
Instead of Bean:: we can use SUPER::. The call
to the function printType() becomes
$cup->SUPER::printType();
and the output is the following:
The type of Bean is Mixed
One advertised strength of object-oriented languages is the
ease with which new code can use old code. Packages in Perl let you
reuse code through the use of objects and inheritance. OOP languages
use data encapsulation to let you hide the inner workings of
complicated code. Packages and modules in Perl provide a great
deal of data encapsulation with the use of the my construct. Perl,
however, does not guarantee that a class inheriting your code
will not attempt to access your class variables directly, thereby
eliminating the advantage of data encapsulation. They can if they really
want to; however, this type of procedure is considered bad
practice, and shame on you if you do it.
DO define methods to access class variables.
DON'T access class variables directly from outside the module.
When writing a package, you should ensure that everything a
method needs is available through the object or is passed as a parameter
to the method. From within the package, access any global variables
only through references passed through methods.
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 local()
construct. The subclass will then call the base class to get the
data for it. On occasion, a subclass might want to override that
data and replace it with new data. When this happens, the
superclass might 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
through that reference.
Finally, you will see references to objects and classes such
as the following:
use Coffee::Bean;
This code is interpreted to mean "Look for Bean.pm
in the Coffee subdirectory in all the directories in the @INC
array." If I were to move Bean.pm into the ./Coffee
directory, all the previous examples would work with the new use
statement. The advantage to this approach is that you have one
subclass class file in one directory and the base class in a
lower directory. It helps keep code organized. To have a
statement like the following:
use Another::Sub::Menu;
you would see a directory sub-tree like this:
./Another/Sub/Menu.pm
This chapter provides a brief introduction to object-oriented
programming in Perl. Perl provides the OOP features of data encapsulation
and inheritance using modules and packages. A class in Perl is simply
a package. A package for a class provides all the methods for
objects created for the class.
An object is simply a reference to data that knows which class
it belongs to. A method in a class is simply a subroutine. The only
catch about writing such methods is that the name of the class is always
the first argument of the method.
The bless() function is used to tie a reference to a
class name. The bless() function is called in the constructor
function new() to create an object and then connect the
reference to the object with the name of the class.
With inheritance, the base class is the class from which
methods (and data) are inherited. The base class is also called
the superclass. The class that inherits these items from the
superclass is called the subclass. Multiple inheritance is
allowed in Perl. Data inheritance is the programmer's responsibility and
requires using references. The subclass is allowed to know things about
its immediate superclass; the superclass is not allowed to know
anything about a subclass.
Balloon::new() Balloon->new() new Balloon;
{
my $x; my $y;
$x = \$y;
}
$zy = $year;
$zm = ($month + 10) % 12;
$zy-- if ($m > 10);
$zc = int ( $y / 100 );
$yy = $year % 100;
$zeller = ( int ( (26*$zm - 2)/10) + $dayOfMonth
+ $yy + int($yy/4)
+ int ($zc/4) - 2* $zc ) % 7;
sub makeCup {
my ($class, $cream, $sugar, $dope) = @_;
print "\n================================== \n";
print "Making a cup \n";
print "Add cream \n" if ($cream);
print "Add $sugar sugar cubes\n" if ($sugar);
print "Making some really nice coffee ;-) \n" if ($dope);
print "================================== \n";
}
Imprint: SAMS
TY Perl 5 in 21 Days, 2E, 30894-0, 19
FileName: TYP19YB.W6W
MS Pages: 13
Listing Pages: 10
Figure Count: 0
------------ -----
Total Pages: 23
Last Update: 03/08/96 10:23 AM
???Should the preceding head be a subheading of Class in Perl?