Perl Free Tutorial

Web based School

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.

An Introduction to Modules

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 Three Important Rules

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.

Classes in Perl

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.

Creating a Class

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.

Blessing a Constructor

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.

Instance Variables

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";

Methods

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.

Exporting Methods

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.

Invoking Methods

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.

Overrides

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);

Destructors

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.

Inheritance

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.

Overriding Methods

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

A Few Comments About Classes and Objects in Perl

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

Summary

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&A

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.

Workshop

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.

Quiz

  1. Show at least three ways to create a new object of a given class, Balloon.
  2. What's wrong the following lines of code?
    {
    my $x; my $y;
    $x = \$y;
    }
  3. What are the three most important rules about OOP in Perl?
  4. How do you override a call to a method to use the base class instead of the subclass?

Exercises

  1. 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;
  2. 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.
  3. Create a class to list the entire directory tree when given a path name.
  4. 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";
    }