Search code examples
perloopconstructorradix

Calling base constructor in perl


What is the correct way to call the base constructor from the class constructor in Perl ?

I have seen syntax like this:

 my $class = shift; 
 my $a = shift; 
 my $b = shift;
 my $self = $class->SUPER::new($a, $b);
 return $self;

Is this correct ? What if we have several parent classes. For example a class like this:

 package Gamma;
 use base Alpha;
 use base Beta;

 sub new
 {
   # Call base constructors...
 }
 1;

Solution

  • This problem is why some people recommend not doing anything interesting in your new method. new is expected to create and return a blessed reference, it's hard to make a system that handle doing this twice for the same object from different parent classes.

    A cleaner option is to have a new method that only creates the object and calls another method that can set up the object. This second method can behave in a way that allows multiple parent methods to be called. Effectively new is you allocator and this other method is your constructor.

    package Mother;
    use strict;
    use warnings;
    
    sub new {
        my ($class, @args) = @_;
        my $self = bless {}, $class;
        return $self->_init(@args);
    }
    
    sub _init {
        my ($self, @args) = @_;
    
        # do something
    
        return $self;
    }
    
    package Father;
    use strict;
    use warnings;
    
    sub new {
        my ($class, @args) = @_;
        my $self = bless {}, $class;
        return $self->_init(@args);
    }
    
    sub _init {
        my ($self, @args) = @_;
    
        # do something else
    
        return $self;
    }
    
    package Child;
    use strict;
    use warnings;
    
    use base qw(Mother Father);
    
    sub _init {
        my ($self, @args) = @_;
    
        # do any thing that needs to be done before calling base classes
    
        $self->Mother::_init(@args); # Call Mother::_init explicitly, SUPER::_init would also call Mother::_init
        $self->Father::_init(@args); # Call Father::_init explicitly, SUPER::_init would NOT call Father::_init
    
        # do any thing that needs to be done after calling base classes
    
        return $self;
    }
    

    Ether is right about the complications your likely to find by using multiple inheritance. You still need to know that Father::_init won't override any decisions made by Mother::_init to start with. It will only get more complicated and harder to debug from there.

    I would second the recommendation of Moose, its interface includes a better separation of the object creation and initialization than my example above that usually just works.