Camel POOP
Most people are not aware of the fact that Perl has support for object-oriented programming. If you've used another object-oriented programming language such as Java or C++ or been exposed to object-orientation, then object oriented programming in Perl is nothing like that. To do real useful object-oriented programming in Perl, you only need to use three simple rules as put forth by Larry Wall in Object Oriented Perl.
Object oriented programmers are familiar with the concept of object and classes, but I will review that here quickly. An object is a thing that provides access to or modification of data. A class is a description of the attributes of a particular kind of object and the manner in which those objects can be accessed and modified. A method is a means by which an object's data is accessed or modified. An object is an instance of a class.
An example would be a Person
class in an HR system. The Person
class describes the attributes of a person such as name, address, title, social security number, ID, etc. A particular class instance or object would encapsulate data about a particular person, e.g., name, title, social security number, address, etc. Some methods to access that object's data would be name
, address
, etc.
Package Delivery
To create a class in Perl, we first build a package. A package is a self-contained unit of user-defined variables and subroutines, which can be re-used over and over again. They provide a separate namespace within a Perl program that keeps subroutines and variables from conflicting with those in other packages.
To declare a class named Person
in Perl, we do:
package Person;
That's it! The scope of the package definition extends to the end of the file, or until another package
keyword is encountered. Not very useful yet, but on to the next section.
There's a Method to this Madness
A method is a means by which an object's data is accessed or modified. In Perl, a method is just a subroutine defined within a particular package. So to define a method to print our Person
object, we do:
sub print {
my ($self) = @_;
printf( "Name:%s %s\n\n", $self->firstName, $self->lastName );
}
The subroutine print
is now associated with the package Person
. To call the method print
on a Person
object, we use the Perl "arrow" notation. If the variable $khurt
contains a Person
object, we would call print
on that object by writing:
$khurt->print();
When the object method is invoked, a reference to the object is passed in along with any other arguments. This is important since the method now has access to the object on which it is to operate.
How do we create the invoking object?
Bless Me Father
To create an instance of a class (an object), we need an object constructor. This constructor is a method defined within the package. Most programmers choose to name this object constructor method new
, but in Perl one can use any name.
One can use any kind of Perl variable as an object in Perl. Most Perl programmers choose either references to arrays or hashes.
Let's create our constructor for our Person
class using a Perl
hash reference;
sub new {
my $self = {
_firstName => undef,
_lastName => undef,
_ssn => undef,
_address => undef
};
bless $self, 'Person';
return $self;
}
What have we done? We created a subroutine called new
associated with the package Person
. The entries of the hash reference $self
become the attributes of our object. We then use the bless
function on the hash reference. The bless
function takes two arguments: a reference to the variable to be marked and a string
containing the name of the class. This indicates that the variable now belongs to the class Person
.
To create an instance of our Person
object:
my $khurt = new Person();
We have not defined accessor methods or done any error checking on the input values or keys or the anonymous hash reference, but we have the start of a Perl Person
OO framework. To make our constructor more flexible and to make our class inheritable (more on that later), we can define it to use the $class
variable to bless the hash reference.
sub new {
my ($class) = @_;
my $self = {
_firstName => undef,
_lastName => undef,
_ssn => undef,
_address => undef
};
bless $self, $class;
return $self;
}
Other object-oriented languages have the concept of security of data to prevent a programmer from changing an object data directly and so provide accessor methods to modify object data. Perl does not have private
variables but we can still use the concept of accessor methods and ask programmers to not mess with our object innards.
For our Person
class, we should provide accessor methods for our object attributes; name
, address
, title
, SSN
.
package Person;
use strict;
use Address;
sub new {
my ($class) = @_;
my $self = {
_firstName => undef,
_lastName => undef,
_ssn => undef,
_address => undef
};
bless $self, $class;
return $self;
}
sub firstName {
my ( $self, $firstName ) = @_;
$self->{_firstName} = $firstName if defined($firstName);
return $self->{_firstName};
}
sub lastName {
my ( $self, $lastName ) = @_;
$self->{_lastName} = $lastName if defined($lastName);
return $self->{_lastName};
}
sub address {
my ( $self, $address ) = @_;
$self->{_address} = $address if defined($address);
return $self->{_address};
}
sub ssn {
my ( $self, $ssn ) = @_;
$self->{_ssn} = $ssn if defined($ssn);
return $self->{_ssn};
}
sub print {
my ($self) = @_;
printf( "Name:%s %s\n\n", $self->firstName, $self->lastName );
}
1;
Making Babies
Object-oriented programming sometimes involves inheritance. Inheritance simply means allowing one class called the Child
to inherit methods and attributes from another, called the Parent
, so you don't have to write the same code again and again. For example, we can have a class Employee
which inherits from Person
. This is referred to as an "isa
" relationship because an employee
is a person
. Perl has a special variable, @ISA
, to help with this. @ISA
governs (method) inheritance. So to create a new Employee
class that will inherit methods and attributes from our Person
class, we simply code:
package Employee;
use Person;
use strict;
our @ISA = qw(Person);
What we have done is load the Person
class and declare that Employee
class inherits methods from it. We have declared no methods for Employee
but an Employee
object will behave just like a Person
object. We should be able to write code:
my $khurt = new Employee();
$khurt->firstName('Khurt');
$khurt->lastName('Williams');
without any other changes.
Now let's add some methods.
package Employee;
use Person;
use strict;
our @ISA = qw(Person);
sub new {
my ($class) = @_;
my $self = $class->SUPER::new();
$self->{_id} = undef;
$self->{_title} = undef;
bless $self, $class;
return $self;
}
sub id {
my ( $self, $id ) = @_;
$self->{_id} = $id if defined($id);
return ( $self->{_id} );
}
sub title {
my ( $self, $title ) = @_;
$self->{_title} = $title if defined($title);
return ( $self->{_title} );
}
sub print {
my ($self) = @_;
$self->SUPER::print;
$self->address->print;
}
1;
Looking at the code, you will notice that we have a new
method and a print
method. Both the child class and its parent class have the same method defined. We have overridden the parent class' methods with the ones from the child. When those methods are called on an Employee
object, we will get the Employee
class' version of the method. This concept of using the methods of an existing object and modifying them is known as polymorphism.
Putting It Together
So now that we have a complete set of classes, we can write a small program to test them.
use strict;
use warnings;
use diagnostics;
use Employee;
my $khurt = eval { new Employee(); } or die ($@);
$khurt->firstName('Khurt');
$khurt->lastName('Williams');
$khurt->id(1001);
$khurt->title('Executive Director');
$khurt->address( new Address() );
$khurt->address->street('10 Anywhere Lane');
$khurt->address->city('Anytown');
$khurt->address->state('NJ');
$khurt->address->zip('12345');
$khurt->print();
Let's execute our code and see the output:
$ ./test.pl
Name:Khurt Williams
Address:10 Anywhere Lane
Anytown, NJ 12345
It works! We covered the basics of object oriented programming in Perl and I hope this article was informative and useful.
Khürt Williams is an independent information systems security consultant working for private industry and government in the Princeton area. He holds CISSP, CRISC and ITIL certifications. You can contact Khurt at khurt-at-islandinthenet.com or visit http://islandinthenet.com.