Object-Oriented Programming

One of the major topics I didn't cover in this book is the use of Perl for object-oriented programming, or OOP (if this was Sams Teach Yourself Perl in 25 and a Half Days, we might have been able to cover it). Fortunately, Perl makes object-orientation easy by using familiar Perl features such as packages, subroutines, and references to implement an object-oriented programming environment. This way, if you know something about OOP, you can start programming right away by following only a few rules. If you don't know object-oriented programming, you have some basic background to catch up on, but then you won't have any other major new language features of Perl to learn to be able to use OOP skills right away.

Getting Started and Learning More

If you're unfamiliar with object-oriented programming but want to do some, your best first step is to learn about the basic concepts. Object-oriented programming is simply a different way of looking at the same programming problem. Everything you've learned so far in this book about syntax and good programming practice still applies; the difference is how your whole larger script is organized and how it behaves.

The central notion behind object-oriented programming is that instead of your script being a collection of sequentially executed statements and subroutines, it is a collection of objects that interact with each other in some predefined way. Each object has a defined appearance or state (variables, or properties in OOP jargon) and a defined set of behaviors (subroutines, called methods in OOP parlance). Objects get this template of behavior and state from a class definition. That class definition, in turn, often automatically inherits (uses) features from one or more other classes. When you build an object-oriented script, you use Perl to create one or more of your own classes, which import and use classes and objects from other sources (usually modules). When your Perl script runs, runtime objects are created from the various classes that modify each other's variables and call each other's subroutines to produce some result at the end.

If that previous paragraph scared the living daylights out of you, don't panic. Perl makes it easy to learn about OOP slowly, to use only some features of OOP without having to learn about everything at once. And there are lots of OOP tutorials out there to help you grasp the concepts and the theory. The following are some good places to start:

  • The definitive text for writing object-oriented programs in Perl is Damian Conway's Object Oriented Perl, which is published by Manning. It's not an easy read, but if you're serious about object-oriented Perl, it's the book to get.

  • The perltoot (object-oriented tutorial) man page comes with Perl and offers a basic tutorial and Perl background for object-oriented programming. From there, the perlobj (Perl Objects) and perlbot (“bag of tricks”) man pages will fill in the gaps.

  • Many books that deal with other OOP languages such as Java or C++ might offer basic OOP background chapters, if you've already got those books lying around or if you can borrow one from a friend. I wrote one for Sams Teach Yourself Java in 21 Days that may be of use. OOP concepts can usually be applied from one language to another.

If you're still boggled, don't panic. Although OOP is considered the wave of the future by many, if you're comfortable working in plain-old Perl and no one at a job is demanding that you learn OOP, there's nothing wrong with sticking to what you know. There's more than one way to do it.

The Basics (for Those Who Already Know OOP)

So you know what an object is, how it relates to a class, and you're familiar with the terms instance variable, method, inheritance, constructors, destructors, and encapsulation. Let's apply that terminology to Perl.

Using Classes, Objects, and Object References

In Perl, a class is a package, and the namespace defined by the package defines the encapsulation and scope for that class. Variables defined in that package are class (static) variables; instance variables are typically created by using a reference to a hash, keyed by instance variable name. Class and instance methods are both defined by using subroutines; the only difference between a method and a regular subroutine is that a method assumes its first argument will be a classname (for class methods) or an object reference (for instance methods).

An object reference? Indeed. This is the one bit of new Perl information you need to know to use OOP in Perl. An object in Perl is effectively a reference to some data structure (typically an anonymous empty hash) that has been specially marked so that it behaves as an object and knows to which class it belongs. To mark something as an object, you use the built-in bless function. The bless function returns a reference to an object that you can then assign to a scalar variable the way you would any reference.

Typically, you'll use bless in the constructor for your class. That constructor is simply a subroutine conventionally called new. The bless function takes two arguments: the thing for which you want to create an object reference and the name of a class. A simple class definition, then, might look something like the following:

package MyClass;
sub new {
   my $classname = shift;
   my $self = {} ;
   return bless $self, $classname;
}

Here, the new class method is assumed to be called with one argument, a classname. Inside new we create an empty anonymous hash, bless it with the current classname, and return that reference. Note that this is a class method, and class methods always have the name of the class as their first argument.

Note

You can also use bless with a single argument (the thing to bless), and Perl will use the name of the current class for the second argument. However, because of the way Perl operates on inherited methods, using the single-argument bless can result in wrong results if some other class inherits your constructor. It's generally a good idea to get into the habit of using the two-argument version of bless and to get the classname from the argument list for the method.


To create and use an object (from this class or from any other class), you'd call the new method with the package (class) name. You can do it in the same file as your class definition, as long as you switch to package main first:

package main;
$obj = new MyClass;

The scalar variable $obj then contains a reference to the object defined by the class MyClass. Alternately, you can use an alternate syntax with -> to call the new constructor:

$obj = MyClass->new();

The two different ways of calling new are exactly the same; however, if your new constructor requires arguments other than the classname, it's often easier to use the latter format:

$obj = MyClass->new(12, 240, 15);

To call methods defined in your new object, you'd simply dereference the object reference. The -> syntax works particularly well here:

$obj->aSubroutine('foo','bar'),

Alternately, you can use a more standard function call syntax for methods, where the classname or an object reference must be the first argument to that method:

aSubroutine $obj, 'foo', 'bar';

In this case, because the first argument is an object reference, Perl will dereference the reference for you and call the right method.

You can also call a method as if it were a function inside a package (Myclass::aSubroutine()), but don't do that unless you know what you're doing. You're subverting the OOP-ness of the class when you do that, and you lose the ability to get at a method definition available through inheritance. More about defining and calling references in the next section, “Instance Variables.”

Instance Variables

When you create an object by using this example, you use an anonymous hash as the “thing” to be blessed into an object. Why a hash? Because you can use that hash to store and access instance variables, and you'll get a new version of those variables each time. This is how all internal object state is stored in Perl objects.

Note

Some OOP languages make a distinction between instance variables and class variables (the latter is sometimes called static data). Perl does not have class variables per se. Although you can always create package global variables to represent class variables (and access them through normal package syntax ($MyClass::myclassvar), those variables are not inherited and can be accessed or changed without restriction by any user of your class. Try to work around the use of class variables by using instance variables instead.


Generally, a class's instance variables are defined and initialized in the constructor for your class. In fact, you might want to pass arguments to new that represent the initial values of that class:

#!/usr/bin/perl -w

package Rectangle;
sub new {
   my ($classname, $w, $h) = @_;
   my $self = {} ;
   $self->{Width}  = $w;
   $self->{Height}  = $h;
   return bless $self, $classname
}
sub area {
   my $self = shift;
   return $self->{Width}  * $self->{Height} ;
}

package main;
$sq = new Rectangle (12, 20);
print "Area: ", $sq->area(), "
";

In this example, we used a class constructor that takes two extra arguments—a width and a height—with which it constructs the rectangle object, storing those values in the object's hash keyed by the strings Width and Height. We also created an area method that multiplies the current values of those instance variables and returns the result.

Generally, Perl's model for instance variables is that to access or modify the values of those variables, you create and use methods to do so rather than modifying those values yourself through direct assignment. This has advantages for inheritance, and some would argue provides more of a “pure” object-oriented interface to your class or object. You can, however access instance variables by using regular dereferencing syntax:

# non OOPy variable access
print "Width: $sq->{Width} 
";
print "Height: $sq->{Height} 
";
							

Inheritance

Similar to any respectable object-oriented programming language, Perl provides class inheritance to allow classes to automatically make use of and expand on the definitions of other classes. If class B inherits behavior from class A, class A is called the superclass or base class. Class B, in turn, is called the derived class or subclass.

To indicate that a class inherits from some other class, use the special array @ISA. The @ISA array indicates in which classes to search for method definitions if a definition for a method being called does not exist in the current class. So, for example, if you had a superclass called Feline, a subclass called Lion might look like this:

package Lion;
@ISA = qw( Feline );
...

The call to qw here isn't really needed when a class inherits from only one superclass, but it does make it easy to add more superclasses later, for multiple inheritance:

package HouseCat;
@ISA = qw( Feline Pet );
...

When a method is invoked on a particular object and no definition of that method is found in the current class definition, Perl looks for a method definition in each of the listed superclasses, depth first, in the @ISA array. In other words, the method eat, invoked on the HouseCat class, would first look for a definition in HouseCat itself, then in Feline, and then in all the superclasses of Feline, if any, and in those classes' superclasses, before looking for that definition in Pet. The first definition found is the one that will be used.

Perl only provides method inheritance. For “inherited” instance variables you can use inherited constructor methods to build a single hash of instance variables, which is made up of all the superclasses' instance variables plus any defined for the current class (the perlobj man page includes an example of this).

Defining Methods

There are three general conceptual ways to look at defining a method:

  • New methods specific to the current class

  • Methods that override methods defined in superclasses

  • Methods that expand on or add to the behavior of a superclasses' methods

Regular methods that don't make use of inheritance are defined as regular subroutines inside the class (package) definition. The only assumption you need to make concerns the first argument to that subroutine, which determines whether the method is a class or instance method.

For instance methods, the first argument is an object reference. Typically, the first thing you do in these methods is extract that reference from the argument list and store it in a scalar variable ($self is a very common variable name, but you can call it anything you like):

sub myMethod {
   my $self = shift;
...
}

For class methods, the first argument is simply the name of the class—just a string, nothing special. Again, generally you'll want to extract and save it in a scalar variable.

What about methods that could be class or instance methods, depending on their argument? You'll need a way to test that argument inside the body of the method to see if it's an object or a class. The ref function works well this way:

sub classOrInstance {
  my $arg = shift;
  if (ref($arg)) {
     print "Argument is an object
";
  }  else {
     print "Argument is a class name
";
  }
}

Note that the ref function, given an object reference as an argument, returns the name of the class of which the object is an instance.

Invoking methods defined in the current class will execute that method—even if there's a method of the same name further up in the inheritance chain. This is how you define methods that override existing methods—just define it and you're done.

If you want to add to the behavior of some superclass's method rather than override it altogether, a special package called SUPER tells Perl to search for a method definition in the classes defined by @ISA:

sub calculate {
   my $self = shift;
   my $sum = $self->SUPER::calculate(); # do superclass first

   foreach (@_) {
      $sum += $_;
   }
   return $sum;
}

Note

One common use of SUPER is in inherited constructors (new methods). With SUPER, you can create a constructor that runs up the chain of inheritance to make sure the current object has all the instance variables and default state of those variables that it needs.


You've already seen how to create constructor methods by using bless; you can also create destructor methods, which are executed after all references to the object have gone away and it's just about to be garbage collected. To create a destructor method, define a subroutine called DESTROY in your class:

sub DESTROY {
   print "Destroying object
";
   ...
}

Autoloaded Methods

One nifty feature of Perl's method invocation is that of autoloaded methods. Autoloaded methods are sort of the methods of last resort—if you invoke a method on an object and Perl cannot find any existing definition for that method, it will attempt to call a method called AUTOLOAD instead. The package variable $AUTOLOAD will contain the name of the method that was called (including the original classname from which it was called); you can then use that information to do something for otherwise unknown methods. The perlobj man page shows a great example of using autoloaded methods as accessor methods for instance variables without having to define separate methods for each one. Here's a simple example of an autoloaded method to handle this sort of behavior:

package HouseCat;
@ISA = qw( Feline, Pet );

sub new {
  my $classname = shift;
  return bless {} , $classname;
}

sub AUTOLOAD {
   my ($self,$arg) = @_;
   my $name = $AUTOLOAD;

   my $name =~ s/.*://;  # get rid of package part
   if ($arg) {  # set value
      $self->{$name}  = $arg;
      return $arg;
   }  else {     # no arg, return value
      return $self->{$name} ;
   }
}

package main;
my $cat = new HouseCat;
$cat->color("Grey");
print "My cat is " . $cat->color() . "
";
							

In this example, the HouseCat class doesn't have a color method (and we're assuming that none of its superclasses has one either). When the color method is called, Perl calls AUTOLOAD instead. For this definition of AUTOLOAD, we get the name of the method that was called through the $AUTOLOAD variable, strip off the package name at the beginning, and then use that method name as an instance variable name. If the method was called with an argument, we'll set that instance variable; otherwise we'll just print the current value (it's up to the caller to manage undefined instance variables). You could just as easily use this AUTOLOAD method for any instance variable—name, age, temperament, favorite_food, and so on.

Note

AUTOLOAD methods aren't technically the methods of last resort; in addition to AUTOLOAD there is also the UNIVERSAL class. UNIVERSAL is used as sort of a global superclass; you can define your last-resort methods there.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset