Chapter 19
Object-Oriented Programming in Perl
by Kamran Husain
CONTENTS
ToChapter'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 are covered on Chapter 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 provides 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.
- A class is a Perl package. This package for a class
provides the methods for objects.
- A method is simply a Perl subroutine. The only catch
with writing such methods is that the name of the class is the
first argument.
- An object in Perl is simply a reference to some data item
within the class.
The rest of toChapter'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 illustrates 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 guide application, but how many similar examples have you
already seen in guides?
| 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 toChapter'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. Creating the constructor.
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 named 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 4
as the following:
$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 it's 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:
- Cocoa::new()
- Cocoa->new()
- new Cocoa;
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;
Â# 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 the object created. Using instance variables
allows you to customize each object as it is created.
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.
| 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 class's 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 the following code:
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 toChapter. 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.4 is the code for Bean.pm.
Listing 19.4. 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.4 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.5.
Listing 19.5. 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.5 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 sub-routine 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.6 shows how to call these functions.
Listing 19.6. 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 processes
them only if it sees them. To test this functionality, consider
Listing 19.7.
Listing 19.7. 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 meaningful only 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.
| Q: | What does the bless() function do?
|
| A: | The bless() function takes one or two arguments. The first argument is a reference to an object. The second argument is optional and specifies the name of a class; if the name is not specified, the
default is the current class. After the call, the reference uses the name as its class name. As a result, the reference becomes an object of the class whose name was specified.
|
| Q: | What's the difference between an object and a reference?
|
| A: | Objects are blessed; references are not. Objects belong to a class, but references do not have to.
|
| Q: | What's the difference between static and virtual methods?
|
| A: | Static methods expect a class name as the first argument. Virtual methods expect a reference to an object as the first argument. Static methods are class-wide; virtual methods are object-specific.
|
| Q: | I just added a method to my class file, but it is never called! What's wrong?
|
| A: | Make sure you are using the require Exporter; statement and that the name of the new function is in the @EXPORTER array.
|
The Workshop provides quiz questions to help you solidify your
understanding of the material covered and exercises to give you
experience in using what you've learned. Try and understand the
quiz and exercise answers before you go on to tomorrow's lesson.
- Show at least three ways to create a new object of a given
class, Balloon.
- What's wrong the following lines of code?
{
my $x; my $y;
$x = \$y;
}
- What are the three most important rules about OOP in Perl?
- How do you override a call to a method to use the base class
instead of the subclass?
- Write a simple class to print out the Chapter of the week using
the Zellers congruence formula to get the Chapter of the week given
a date. The following shows the formula in Perl code:
$zy = $year;
$zm = ($month + 10) % 12;
$zy- if ($m > 10);
$zc = int ( $y / 100 );
$yy = $year % 100;
$zeller = ( int ( (26*$zm - 2)/10) + $ChapterOfMonth
+ $yy + int($yy/4)
+ int ($zc/4) - 2* $zc ) % 7;
- Extend the class you just created to allow specifying a date
at creation time where the Chapter, month, year, or all three can
be optional. Hint: Use the date function to get the current date.
- Create a class to list the entire directory tree when given
a path name.
- Modify the following function to print black if no
parameters are passed to it: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";
}

|