tags:

views:

49

answers:

3

I am doing a Perl OO example (mainly to reacquaint myself with Perl) and using inheritance, it seems that the SUPER works in the subclass when calling the superclass constructor and that the superclass variables don't bind to the subclass object. Why would this be?

################################################
## Package : Person                          ##
################################################
package Person;

################################################
## Constructor                                ##
################################################
sub new(){

    my $class = shift;
    my $data = {};
    $data->{'firstName'} = shift;
    $data->{'secondName'} = shift;  
    bless $data, $class; 
    #bless $data, "Person"; #hack
    return $data;
}

################################################
## Getter Methods                            ##
################################################

sub getFirstName(){
  my $data = shift;
  return $data->{'firstName'};
}

sub getSecondName(){
  my $data = shift;
  return $data->{'secondName'};
}

################################################
## Setter methods                            ##
################################################

sub setFirstName($){
  my $data = shift;
  $data->{'firstName'} = shift;
}

sub setSecondName($){
  my $data = shift;
  $data->{'secondName'} = shift;
}

################################################
## Other Methods                              ##
################################################

sub printall(){
    my $data = shift;
    $\ = "\n";
    print "FirstName: ". $data->{'firstName'} ."\n";
    print "SecondName: ". $data->{'secondName'} ."\n";
}
1;

################################################
## Package : Coder                            ##
################################################

package Coder;
@ISA = qw( Person );
use strict;
use warnings;

################################################
## Constructor                                ##
################################################
sub new {
    my $class = shift;
    my $self = {};

    bless $self, $class;
    my $superFirstName = shift;
    my $superSecondName = shift;

    print "new superfirstname "  .$superFirstName;
    print "new supersecondname " .$superSecondName; 

    $self->{'language'} = shift; #i.e. Java 
    $self->{'experience'} = shift;  #number of years

    #$self = $self->SUPER::new($superFirstName, $superSecondName); 
    Person->new($superFirstName, $superSecondName);
    return $self;
}

################################################
## Getter Methods                            ##
################################################

sub getLanguage(){
  my $data = shift;
  return $data->{'language'};
}

sub getExperience(){
  my $data = shift;
  return $data->{'experience'};
}

################################################
## Setter methods                            ##
################################################

sub setLanguage($){
  my $data = shift;
  $data->{'language'} = shift;
}

sub setExperience($){
  my $data = shift;
  $data->{'experience'} = shift;
}

################################################
## Other Methods                              ##
################################################

sub printall(){
    my $data = shift;
    $\ = "\n";

    print "Experience: " . $data->{'experience'};
    print "Language: " . $data->{'language'};

    $data->SUPER::printall();

}
1;


################################################
## Package : Main                            ##
################################################
package main;

my $developer = Coder->new("John","Smith","Perl","2");

$developer->printall();
+3  A: 

If you are intending that Coder inherits from Person, you are constructing it incorrectly. For example, in Coder's constructor, you are creating a new Person object and then throwing it away. This would be a much better (and more standard) way of constructing a Coder object that inherits from Person (see perldoc perltoot):

sub new
{
    my $class = shift;
    my $this = $class->SUPER::new(shift, shift);

    # add on extra fields, now at the head of @_...

    bless $this, $class;
    return $this;
}

There are also many improvements you could make:

  • missing use strict; use warnings; in Person
  • use parent 'Person'; rather than manipulating @ISA
  • function prototypes are rarely a good idea (only use them if you know you need them, and this is never true when writing an object-oriented class where functions will be called as methods)
  • you can automatically generate accessors and mutators with Class::Accessor, rather than writing a lot of duplicated boilerplate

Also, although I'm not sure you're ready (you should understand the fundamentals of Perl OO construction first), you can skip writing almost all this code by using Moose:

package Person;
use Moose;
has [qw(firstName secondName)] => (
    is => 'rw', isa => 'Str',
);
no Moose;
__PACKAGE__->meta->make_immutable;
1;

package Coder;
use Moose;
extends 'Person';
has [qw(language experience)] => (
    is => 'rw', isa => 'Str',
);
no Moose;
__PACKAGE__->meta->make_immutable;
1;
Ether
can you explain the "no Moose"?
ysth
@ysth it unimports the moose sugar functions (like `has`, `extends`, `with`, `before`/`after`/`around` etc.) so that no one can accidentally call `Yourclass->has(...)` later on (or so that they don't accidentally shadow similarly-named methods from your superclasses, which would be even worse). [namespace::autoclean](http://search.cpan.org/perldoc/namespace::autoclean) is another way of dealing with the situation.
hobbs
+3  A: 

It's because you are not doing anything with the Person->new return. So it just creates a whole other Person object, and Coder skates off happily.

What you want to do is

my $self = $class->SUPER::new( $superFirstName, $superSecondName );

And have Person bless it into whatever class you pass it--as it does. Then after creating $self the SUPER way, you want to add to it the fields you need.

So it should look something like:

use strict;
use warnings;

sub new {
    my $class = shift;
    my $superFirstName = shift;
    my $superSecondName = shift;
    print "new superfirstname "  .$superFirstName;
    print "new supersecondname " .$superSecondName; 

    my $self = $class->SUPER::new( $superFirstName, $superSecondName );
    # OR
    # $self = $class->Person::new( $superFirstName, $superSecondName );

    $self->{language}   = shift; #i.e. Java 
    $self->{experience} = shift;  #number of years

    return $self;
}
Axeman
Thanks this worked! $self = $class->Person::new( $superFirstName, $superSecondName );I used this line and it did the job, many thanks, I wish I had a better reputation to give you points.
+2  A: 

The constructor for Coder doesn't make sense; you're calling Person->new and then throwing away the result. Here's a more useful pattern:

package Coder;

sub new {
  my $class = shift;

  my $self = $class->SUPER::new(shift, shift); # firstname, lastname

  $self->{language} = shift;
  $self->{experience} = shift;

  $self;
}

Yes, the Person constructor will bless the object into Coder in this case, not into Person -- that's the point of having the class as the first argument to new.

This is the minimum-changes version of your code, but I'd like to make a few suggestions beyond that:

  1. Positional parameters to the constructor are a great way to make sure no one knows what's going on. I'd suggest typing it as Person->new(first_name => "Joe", last_name => "Random") etc.

  2. You might be better off forgetting all of this drudge work and using Moose :)

hobbs