Item 49: Consider using Perl s object-oriented programming features.


Item 49: Consider using Perl's object-oriented programming features.

Perl contains all the features needed to support object-oriented programming, including the following:

  • Classes In Perl, packages are classes.

  • Objects Perl's objects are ordinary Perl data types like hashes and arrays that have been "blessed" into a package.

  • Methods Perl's methods are ordinary subroutines that are called using a special method call syntax.

  • Constructors These are subroutines that return a reference to a newly created and initialized object.

  • Destructors These are subroutines that are called when an object goes out of scope or is no longer referenced by any other object.

  • Inheritance Perl supports class inheritance, both single and multiple. Perl does not support data inheritance, but this is not an onerous limitation (see Item 51).

  • Overloading Methods can be overloaded very simply in Perl, and an overload pragma module allows operator overloading along the lines of the feature available in C++.

In addition, it is possible to approximate many other quasi-object-oriented featureslike templatesin Perl.

An object-oriented example

The easiest way to approach object-oriented programming in Perl is to see an example of it. Here is a minimalist Perl class called Timer :

Writing a simple class in Perl

This class implements a Timer object that reports the number of seconds since it was created.

Start by setting the default package to Timer :

 package Timer; 
 

Next is the constructor for Timer objects.

 sub new {    my $pkg = shift;    bless { created => time }, $pkg;  } 

new is lowercase by convention. First arg is package name . Return blessed hash ref containing current time.

Now, a method to return the time elapsed since creation:

 sub Elapsed {    my $self = shift;    return time - $self->{created};  } 

First arg is object (see below).

Return difference between "now" and "then."

Here is an example of how the Timer class could be used:

Using a class in Perl

 package main;  $timer = new Timer;  sleep 5;  print "elapsed: ", $timer->Elapsed(), "\n"; 

Create a Timer object.

Do some timing.

Use the Elapsed method to see how long it took.

Although this is a very short example, it covers all of the most important aspects of object-oriented programming in Perl (except for inheritance, which is covered in Item 50). Bear this example in mind as I launch into a somewhat long-winded discussion of it below.

A class in Perl is a package (see Item 42). The concept of class in other object-oriented languages is similar, in that a separate namespace is one of the most important facilities provided by a class.

A Perl object is data that has been blessed into a package. The bless operator takes a reference (usually, but not necessarily , a hash reference) and a package name as arguments. The package name is optionalthe current package is used if it is omitted. When blessed, data " knows " to which package it belongs. In PEGS, blessed data is topped off with a rounded-corner rectangle containing the package name:

graphics/10fig01.gif

The ref operator (see Item 30) returns the package name rather than the type of data when it is applied to a blessed reference: [1]

[1] For the buzzword -compatible user , this is RTTI (Run-Time Type Identification)

The ref operator returns the package name of a blessed object.

Continued from above:

 print "timer is a: ", ref $timer, "\n"; 

$timer is a Timer .

This can be handy information but it should not be overused , because a major goal of object-oriented programming is to avoid explicitly testing the type of an object.

It is important to realize that although the bless operator takes a reference as an argument, it is not blessing the reference. It is blessing the data to which the reference points :

The bless operator blesses data, not references.

Continued from above:

 my $timer2 = $timer;  print "timer2 is a: ", ref $timer2, "\n"; 

Make a copy of $timer .

$timer2 is still a Timer .

In PEGS, this looks like:

graphics/10fig02.gif

Returning to the example, the first subroutine that we define is a constructor. A constructor is a subroutine that creates and initializes a new object. In general, Perl constructors return references to anonymous hashes. This is a convention, and a convenient one, because hashes are a natural way to express "structs" or "records" in Perl (see Item 33).

Another convention is that constructors are generally named new . Unlike some other object-oriented languages, Perl has no special object-constructing or memory-allocating operator. A constructor could be named something entirely different, for example, gimme_a_new , and it would still work in the same way. Naming a constructor new makes object-oriented Perl look familiar to programmers who have worked in other object-oriented languages.

Constructors are methods. You will remember, from the discussion of object-oriented Perl above, that methods are ordinary subroutines that are invoked with a special method call syntax. The line

 $timer = new Timer; 

is an example of "indirect object" method call syntax. Perl interprets a subroutine name followed immediately by a package name in a special way as a call to a subroutine in that package, with the package name as the first argument. So the above line is equivalent to:

 $timer = Timer::new('Timer'); 

Methods invoked by package name are referred to as " static," " class," or " package" methods, according to your preference of nomenclature .

The second subroutine that we define, Elapsed , is also a method. Unlike the constructor, it is designed to be called via an object rather than a package. Although you can use indirect object syntax, the more common technique is to call it using the other type of method call syntax, the " arrow":

 $elapsed = $timer->Elapsed(); 

Here, Perl uses the class of the $timer object to the left of the arrow to determine in which package to look for the subroutine Elapsed . [2] Because $timer has been blessed into package Timer , Perl calls the subroutine Timer::Elapsed . Perl also automatically uses the value to the left of the arrow as the first argument to the subroutine, that is:

[2] Perl will accept a surprisingly large variety of things to the left (and even right) of the method call arrow as well as in the indirect object slot. This behavior is not thoroughly documented as of this writing, but feel free to experiment with things like 'Foo'->Bar() to get a feel for this extra "intelligence."

 $elapsed = Timer::Elapsed($timer); 

Methods invoked via an object rather than a package name are referred to as " object" or " dynamic" methods.

Finally, let's look more closely at the Elapsed method itself. Most object-oriented languages have an automatically created variable called this or self that you can use inside methods. This "self" variable contains a pointer to the object that was used to invoke the method. Perl does not create a $self variable for you automatically. However, as you just saw in our discussion of new and Elapsed , Perl does supply an object or a package name as the first argument to a method. In the case of an object method like Elapsed , it is customary to shift this value off the argument list and name it $self . This is exactly what happens behind the scenes in other object-oriented languages. In the case of the Elapsed method, $self is a reference to a Timer object. This object is an anonymous hash created by the Timer constructor, and therefore $self is a reference to a hash (see Item 30).

Destructors

When the last reference to an object is gone, or the object goes out of scope, the object is destroyed and its space is reclaimed for later use by Perl. For ordinary, non-blessed data, that is the end of the story. Blessed objects, however, can go out with a little more fanfare. If a package contains a subroutine named DESTROY , this subroutine, called a destructor , will be invoked just before a blessed object belonging to that package is destroyed:

 package Foo;  sub new {    my $pkg = shift;    bless { name => shift }, $pkg  }  sub DESTROY {    my $self = shift;    print "nuke '$self->{name}'\n";  } 

Class Foo . . .

Define a constructor.

Define a destructor.

 package main;  $a = new Foo('Bar');  {    my $b = new Foo('Bletch');  } 

nuke 'Bletch' (out of scope)

 $a = new Foo('Baz'); 

nuke 'Bar' (no refs left)

nuke 'Baz' (at termination)

Destruction occurs when an object is no longer referencedeither because the last reference to it was overwritten (as is the case with the second assignment to $a , above) or the last reference to it has gone out of scope (as is the case with $b ). Objects still in existence at program termination are destroyed in a final mark-sweep garbage collection pass, as is the case with the final contents of $a . (For more about memory allocation and garbage collection in Perl, see Item 34.)

Destructors are useful for ensuring that shareable resources like semaphores, locks, and files are released properly when the life of an object that uses them is over. For example:

Use destructors to "clean up."

This illustrates how you might use a destructor to make sure a temporary resource, in this case a file, is released at program termination.

 use FileHandle; 

Use the FileHandle object-oriented file package.

 package TmpFile; 
 
 sub new {    my $pkg = shift;    $uniq++;    my $prog = (split m!/!, 
 sub new { my $pkg = shift; $uniq++; my $prog = (split m!/!, $0)[-1]; my $name = "/tmp/$prog.$$.$uniq"; my $fh = new FileHandle $name, "w+"; die $! unless $fh; bless { name => $name, fh => $fh } } 
)[-1]; my $name = "/tmp/$prog.$$.$uniq"; my $fh = new FileHandle $name, "w+"; die $! unless $fh; bless { name => $name, fh => $fh } }

Construct a TmpFile .

Get program name from $0 .

Create unique filename.

Open file.

Save name and FileHandle .

 sub DESTROY {    my $self = shift;    close $self->{fh} or die $!;    unlink $self->{name} or die $!;  } 

Destroy a TmpFile .

Close the file.

Delete the file.

 sub FH {    my $self = shift;    $self->{fh};  } 

A method to return the FileHandle of a TmpFile .

 package main; 
 
 $temp = new TmpFile;  $fh = $temp->FH;  print $fh "temp data\n";  $fh->seek(0, 0);  print $fh->getline(); 

Create a TmpFile .

Write some data to it and read it back out. The file will be deleted upon termination.



Effective Perl Programming. Writing Better Programs with Perl
Effective Perl Programming: Writing Better Programs with Perl
ISBN: 0201419750
EAN: 2147483647
Year: 1996
Pages: 116

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net