Jan
5
Camel Poop
Filed Under Perl |
In response to the Camel POOP article on Evolt, Simon Willison complains: I have no
intention of starting a language war, but my God this is ugly. Still, I guess it must work for some people.
As a user and fan of Perl, I have to agree. This is exceptionally ugly. In fact it’s the sort of thing that turns people against Perl. Thankfully OO in Perl doesn’t have to be like that.
Let’s take his Person class:
#class Person package Person; use strict; use Address; #Person class will contain an Address #constructor sub new { my ($class) = @_; my $self = { _firstName => undef, _lastName => undef,_ssn => undef,_address => undef }; bless $self, $class; return $self; } #accessor method for Person first name sub firstName { my ( $self, $firstName ) = @_; $self->{_firstName} = $firstName if defined($firstName); return $self->{_firstName}; } #accessor method for Person last name sub lastName { my ( $self, $lastName ) = @_; $self->{_lastName} = $lastName if defined($lastName); return $self->{_lastName}; } #accessor method for Person address sub address { my ( $self, $address ) = @_; $self->{_address} = $address if defined($address); return $self->{_address}; } #accessor method for Person social security number sub ssn { my ( $self, $ssn ) = @_; $self->{_ssn} = $ssn if defined($ssn); return $self->{_ssn}; } sub print { my ($self) = @_; #print Person info printf("Name:%s %snn", $self->firstName, $self->lastName ); } 1;
There are numerous problems with this. Let’s start from the top.
- The ‘use Address’ is completely needless. Misleading comment notwithstanding, nothing in the package actually uses the Address module, so there’s no need to load it.
- The constructor is complete overkill. The object is going to be a hash, but in Perl hash keys autovivify the first time you assign to them, so there’s absolutely no need to set up lots of keys that contain undef. As the constructor does nothing, it could simply be
sub new { bless {}, shift }. - The data methods all do exactly the same thing. This breaks one of the cardinal rules of programming - Don’t Repeat Yourself. firstName, lastName, address, and ssn are all trivial accessor/mutator methods. They could all be abstracted away in a variety of methods, but as this is Perl, someone else has already done that for us. Class::Accessor lets us set up all these methods simply by doing:
Person->mk_accessors(qw/firstName lastName address ssn/); - The constructor doesn’t all you to set object data. It’s pretty much a matter of style, but in general it’s nice to be able to instantiate your object with data, rather than having to call all the mutators in turn. As this is a simple hash-based object (as with 90% of all perl objects) Class::Accessor gives us a default new() as well that allows us to pass a hashref of the data members.
So, this entire class could be replaced with:
package Person;
use strict;
use base 'Class::Accessor';
Person->mk_accessors(qw/firstName lastName address ssn/)
sub print {
my ($self) = @_;
printf "Name:%s %snn", $self->firstName, $self->lastName;
}
1;
Similarly, the Employee subclass, which simply adds ‘id’ and ‘title’ data members, and overrides print could become:
package Employee;
use strict;
use base 'Person';
Employee->mk_accessors(qw/id title/);
sub print {
my ($self) = @_;
$self->SUPER::print;
printf("Name:%s %snn", $self->id, $self->title );
}
1;
(The example on the site makes no sense as it keeps referring to an Address class that doesn’t seem to exist. I’ve assumed that the overridden print should be outputting the extra data members of this class instead…)
Then, instead of the longwinded test program (with a eval to catch exceptions that’s completely needless as neither class throws them):
use Employee;
#create Employee class instance
my $khurt = eval { new Employee(); } or die ($@);
#set object attributes
$khurt->firstName('Khurt');
$khurt->lastName('Williams');
$khurt->id(1001);
$khurt->title('Executive Director');
#diplay Employee info
$khurt->print();
We can simply have:
use Employee;
my $khurt = Employee->new({
firstName => 'Khurt',
lastName => 'Williams',
id => 1001,
title => 'Executive Director',
});
$khurt->print();
It may not be as nice as OO in some other languages, but it’s a lot nicer than the example in the article… and as it’s Perl, there’s plenty of other approaches if you want to dice it another way.