mmmm... dragonchild also thought this was a goofy design and yet I always do
this - a habit tatooed into me when I was a mere Perl love-child.
So I went and checked with some venerated Object Ogres, UML Gods, demi-gods, and Snippet Slayers...
the concerted response was that as a generalisation, an inherited constructor should
ALWAYS ensure that the parent constructor has been executed before doing any localised
specialisation.
So I have cast around for a trivial example that shows why you MUST call the parent class constructor...
This is the output from the code below - $enterprise works, but $voyager blows up!
Starting Enterprise...
StarShip initialised...
Enterprise starship initialised...
Enterprise crew count: 0
Starting Voyager...
Voyager starship initialised...
Can't use an undefined value as an ARRAY reference
at ./trek.pl line 39.
The difference is that Enterprise correctly calls the StarShip constructor, whereas Voyager decides to do its own thing, not realising that they are saving up a warp breach!
#!/usr/local/bin/perl -w
use strict;
# This example shows why child class should always
# execute their parent constructor:
# - Enterprise class gets it right
# - Voyager class blows up
print "Starting Enterprise...\n";
my $enterprise = Enterprise->new(
captain => 'pickard',
compliment => 3000 );
print "Enterprise crew count: ",
$enterprise->getCrewCount(),"\n\n";
print "Starting Voyager...\n";
my $voyager = Voyager->new(
captain => 'janeway',
compliment => 300 );
print "Voyager crew count: ",
$voyager->getCrewCount(),"\n\n";
#---------------------------------------------------
package StarShip;
sub new {
my $class = shift;
my $self = {};
bless $self,$class;
$self->_ssInit(@_);
return $self;
}
sub _ssInit {
my $self = shift;
my %param = @_;
for my $property qw( captain compliment ) {
die "StarShip: missing '$property'"
unless $param{$property};
$self->{$property} = $param{$property};
}
$self->{crew} = [];
print "StarShip initialised...\n";
}
sub getCrewCount {
my $self = shift;
return scalar @{$self->{crew}};
}
#---------------------------------------------------
package Enterprise;
use base qw(StarShip);
sub new {
my $class = shift;
# We perform our parents constructor
# to ensure that our object is in a consistent
# state from the parents perspective
my $self = $class->SUPER::new(@_);
$self->_entInit(@_);
return $self;
}
sub _entInit {
my $self = shift;
$self->{class} = 'constellation';
$self->{id} = 'NCC1701D';
print "Enterprise starship initialised...\n";
}
#---------------------------------------------------
package Voyager;
use base qw(StarShip);
sub new {
my $class = shift;
# Whoops - we didnt call StarShip constructor!
my $self = {};
bless $self,$class;
$self->_voyagerInit(@_);
return $self;
}
sub _voyagerInit {
my $self = shift;
$self->{class} = 'smaller';
$self->{id} = 'VOYAGER1';
$self->jumpTo('gamma quadrant');
print "Voyager starship initialised...\n";
}
sub jumpTo {
my $self = shift;
$self->{x} = rand(-1)*1000;
$self->{y} = rand(-1)*1000;
$self->{z} = rand(-1)*1000;
}
|