Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine

Inheritance confused

by exilepanda (Friar)
on Dec 07, 2015 at 13:02 UTC ( #1149575=perlquestion: print w/replies, xml ) Need Help??

exilepanda has asked for the wisdom of the Perl Monks concerning the following question:

Dear monks, I have few files as follow:

package MyTest::Base; use strict; no autovivification; sub new { bless {}, shift; } sub chkAccessRight { my $self = shift; print "Base chkAccessRight @_$/"; my ( $usr, $service ) = @_ ; return 1; } sub tryAccess { my $self = shift; print "Base tryAccess @_$/"; my ( $usr, $service ) = @_ ; return $self -> chkAccessRight ( $usr => $service ) ; } 1;

package MyTest::Lite; no autovivification; use parent 'MyTest::Base'; use strict; sub new { my $self = shift; my $parent = $self -> SUPER::new ; return bless { Parent => $parent, @_} , $self; } sub chkAccessRight { my $self = shift; print "Lite chkAccessRight @_$/"; if ( $self -> {Usr} ) { return $self->{Parent}->SUPER::chkAccessRight ( $self->{Usr} , + shift ) ; } else { return $self->{Parent}->SUPER::chkAccessRight ( @_ ) ; } return 1; } sub tryAccess { my $self = shift; print "Lite tryAccess @_$/"; if ( $self -> {Usr} ) { return $self->{Parent}->SUPER::tryAccess ( $self->{Usr} , shif +t ) ; } else { return $self->{Parent}->SUPER::tryAccess ( @_) ; } } 1;

use MyTest::Lite; use strict; my $lite = new MyTest::Lite ( Usr => 'guest') ; $lite -> chkAccessRight ( "app2" ) ; print "======================$/"; $lite -> tryAccess ( "app1" );

And run the, I got this output:

Can't call method "SUPER::chkAccessRight" on an undefined value at MyT +est/ line 19. Lite chkAccessRight app2 Base chkAccessRight guest app2 ====================== Lite tryAccess app1 Base tryAccess guest app1 Lite chkAccessRight guest app1

The above codes are snapped (and modified) from my real project, and what I am trying to do is to re-interface the class accessing behavior.

From the last line of output, this is obviously the chkAccessRight() is calling to my overridden sub from MyTest::Lite but I would expect it calls the chkAccessRight() from MyTest::Base

I already have a solution though:
1. Not to use the same method name , so there will be no confusion about the SUPER::, and perl will not attempt to call the overridden method (from Base). BUT, I hope I can maintain the same method name as it seem most appropriate

2. Implement $lite -> tryAccess (  "app1"  ); within the MyTest::Lite class, ie. I dont call the SUPER::tryAccess. BUT some other methods are with more relevance to it's SUPER::* methods, so I like to remain calling the methods' super class for their jobs.

It there any better workaround out there ?

Thanks in advance!



I gave up on the inheritance way, and use the sub AUTOLOAD to return eval "\$self->\{Parent}->$subName\(\@_))". So there will be no more SUPER:: is needed.

Replies are listed 'Best First'.
Re: Inheritance confused
by derby (Abbot) on Dec 07, 2015 at 13:59 UTC

    You do not need to keep a reference to the parent, that's what SUPER does for you. In the constructor, drop the parent and in the methods, just

    that construct will move up the inheritance tree to find the next chkAccessRight method. This construct
    asks the *parent* object to move up it's hierarchy to find the method. Since the parent is the base, there's nowhere to move up to.

      Thanks derby,

      I just modified my code, and you're right, removed the Parent reference also resolve the problem.

      However, I have reason to keep the Base object inside {Parent}, as there are other constructors which will be invoked by other methods in Lite class.

      Since I have the Base reference, then I can make this shareable ( and I need to ) amount other on-the-fly constructed objects. Is there possible to keep my {Parent} ?

        Sure, you could keep the parent just as you have. In your OP, it's not necessary and I haven't had enough coffee this morning to fully evaluate your response but in general keeping a reference like that is pretty much an OO code smell. A class that instantiates objects is normally a Factory but for most Factory classes, that's *all* they do - they do not have other behaviors.

        However, I have reason to keep the Base object inside {Parent}, as there are other constructors which will be invoked by other methods in Lite class.

        There's a better way to do that by making the constructor in the parent class subclass-friendly. This involves checking the first argument you get in an OO constructor routine and identifying if it has a valid ref() associated with it. If you write your constructors as my example code below shows, you'll be making life easy for anyone else who wants to built on your code base.

        In the code below, note a couple of things:

        1. Note that the $ex2 object in is created by using the constructor method of the $ex1 object, which is of the "Improved" subclass.
        2. See how the Parent class uses the ref() call to correctly determine when a subclass is re-using the Parent's constructor method.

        Here's the example code structure:

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1149575]
Approved by Corion
Front-paged by toolic
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (6)
As of 2023-02-08 10:57 GMT
Find Nodes?
    Voting Booth?
    I prefer not to run the latest version of Perl because:

    Results (41 votes). Check out past polls.