Thursday 10 February 2011

Perl: how to inherit and modify the constructor of parent class

This reminder post will be by example, but basically talks about how to inherit a constructor from a parent class, then modify it slightly.  It's also a good case for remembering the reason for this blog - which is to serve as assistance to my memory, and isn't in any way a guidebook or even set of suggestions for how to do things.  This is particularly true of my forays into perl hackery.

Right then.  For this example, we'll start with two simple classes, "Parent" and "Child".  The inheritance relationship works exactly as you'd expect from the names:


Code for inheritance1.pl

#!/usr/bin/perl -w

use strict;

#--------------------------------

package Parent;
sub new {
    my $class=shift;
    my $string1=shift;
    print("1) Called $class constructor with: " . $string1 . "\n");
    my $self={
        string1 => $string1,
        };
    bless($self, $class);
    return $self;
    }

1;

#--------------------------------

package Child;
our @ISA='Parent';
sub new {
    my $class=shift;
    my $string1=shift;
    my $self=Parent->new($string1);
    print("2) Called $class constructor with: " . $string1 . "\n");
    bless($self,$class);
    return $self;
    }

1;

#--------------------------------


my $testObject1=Parent->new("Parent's String");
my $testObject2=Child->new("Child's String");

print("Parent object has stored: ",$testObject1->{string1},"\n");
print("Child object has stored: ",$testObject2->{string1},"\n");

exit 0;


This produces the output:
1) Called Parent constructor with: Parent's String
1) Called Parent constructor with: Child's String
2) Called Child constructor with: Child's String
Parent object has stored: Parent's String
Child object has stored: Child's String




The line of interest here is:
    my $self=Parent->new($string1);

Here the constructor for Child inherits the properties of the constructor for Parent.  As a result, whenever we call the constructor for Child, we see that code from the parent constructor is run.  Additional code from the Child constructor is also run.  However we've really done nothing useful here, as we haven't added any great extra feature to the Child constructor.  So what if we want to store a new parameter into our new object?



Code for inheritance2.pl

#!/usr/bin/perl -w

use strict;

#--------------------------------

package Parent;
sub new {
    my $class=shift;
    my $string1=shift;
    print("1) Called $class constructor with: " . $string1 . "\n");
    my $self={
        string1 => $string1,
        };
    bless($self, $class);
    return $self;
    }

1;

#--------------------------------

package Child;
our @ISA='Parent';
sub new {
    my $class=shift;
    my $string1=shift;
    my $string2=shift;
    my $self=Parent->new($string1);
    print("2) Called $class constructor with: " . $string2 . "\n");
    $self={
        string2 => $string2,
        };
    bless($self,$class);
    return $self;
    }

1;

#--------------------------------


my $testObject1=Parent->new("Parent's String");
my $testObject2=Child->new("Child's 1st String","Child's 2nd String");

print("Parent object has stored: ",$testObject1->{string1},"\n");
print("Child object has stored: ",$testObject2->{string1},"\n");
print("Child object has stored: ",$testObject2->{string2},"\n");

exit 0;


This produces the output:
1) Called Parent constructor with: Parent's String
1) Called Parent constructor with: Child's 1st String
2) Called Child constructor with: Child's 2nd String
Parent object has stored: Parent's String
Use of uninitialized value in print at ./inheritance2.pl line 47.
Child object has stored:
Child object has stored: Child's 2nd String



That didn't work at all!  That shouldn't be too much of a surprise, really, as we've overwritten the declaration of the $self hash.  We could expand it to include everything that the Parent class has, and in this case, it wouldn't take a lot of extra typing, but it's hardly making use of our Object Oriented design, and if there was lots of exciting configuration defined by the $self declaration, we'd lose it all, or have to recreate it.  Better is:



Code for inheritance3.pl
#!/usr/bin/perl -w

use strict;

#--------------------------------

package Parent;
sub new {
    my $class=shift;
    my $string1=shift;
    print("1) Called $class constructor with: " . $string1 . "\n");
    my $self={
        string1 => $string1,
        };
    bless($self, $class);
    return $self;
    }

1;

#--------------------------------

package Child;
our @ISA='Parent';
sub new {
    my $class=shift;
    my $string1=shift;
    my $string2=shift;
    my $self=Parent->new($string1);
    $self->{string2} = $string2;
    print("2) Called $class constructor with: " . $string2 . "\n");
    bless($self,$class);
    return $self;
    }

1;

#--------------------------------


my $testObject1=Parent->new("Parent's String");
my $testObject2=Child->new("Child's 1st String","Child's 2nd String");

print("Parent object has stored: ",$testObject1->{string1},"\n");
print("Child object has stored: ",$testObject2->{string1},"\n");
print("Child object has stored: ",$testObject2->{string2},"\n");

exit 0;


This produces the output:
1) Called Parent constructor with: Parent's String
1) Called Parent constructor with: Child's 1st String
2) Called Child constructor with: Child's 2nd String
Parent object has stored: Parent's String
Child object has stored: Child's 1st String
Child object has stored: Child's 2nd String


And that's just as we expect.

No comments: