Determine if a base routine has been overridden from a base class
This question put me on my guard, but since then I have solved the problem and will post my answer.
I have a base class Parent
Parent
package Parent;
sub new {
my $c = shift;
my $s = {}, bless $s, $c;
return $s;
}
sub who {
return "parent";
}
1;
This parent class provides one method; who
...
I wrote two child classes: Child
and Freak
. Only Child
overrides who
.
Child
package Child;
use base 'Parent';
sub who {
return "child";
}
1;
Freak
package Freak;
use base 'Parent';
1;
How can the base class determine if the base method has been overridden who
?
I want to write something like this
package Parent;
sub new {
my $c = shift;
my $s = {}, bless $s, $c;
return $s;
}
sub who {
return "parent";
}
sub check {
my $self = shift;
my $is_overridden = 1; # what conditional should be here?
return $is_overridden ? "yes" : "no";
}
1;
What conditional value can be used to determine if a subroutine is overridden?
source to share
my $is_overridden = $self->can("who") != Parent->can("who");
See the documentation for UNIVERSAL
that defines can
.
Also, I should add a more philosophical note - it seems that your override violates the Liskov Subscription Principle , it would probably be better to refactor things so that the method override isn't something you need to investigate elsewhere in the code.
source to share
Parent->UNIVERSAL::can('who') ne Child->UNIVERSAL::can('who')
UNIVERSAL::can(PACKAGE,METHODNAME)
returns a reference to a subroutine that will be called when the name of the given method is used in this package. If the packages are different, but the return values ββare the same, this means that one package inherits a method from the other (or they both inherit from a common source). This method works for several levels of inheritance:
package Parent;
sub foo { 42 }
sub bar { 19 }
sub baz { 47 }
package Child;
@Child::ISA = qw(Parent);
sub bar { 19 }
package Grandchild;
@Grandchild::ISA = qw(Child);
sub foo { 42 }
##############
package main;
print "foo:", Parent->UNIVERSAL::can('foo'),
Child->UNIVERSAL::can('foo'),
Grandchild->UNIVERSAL::can('foo'),"\n";
print "bar:", Parent->UNIVERSAL::can('bar'),
Child->UNIVERSAL::can('bar'),
Grandchild->UNIVERSAL::can('bar'),"\n";
print "baz:", Parent->UNIVERSAL::can('baz'),
Child->UNIVERSAL::can('baz'),
Grandchild->UNIVERSAL::can('baz'),"\n";
Typical output:
foo:CODE(0x17e26e8)CODE(0x17e26e8)CODE(0x17e2b38)
bar:CODE(0x17e27a8)CODE(0x17e29b8)CODE(0x17e29b8)
baz:CODE(0x17e2850)CODE(0x17e2850)CODE(0x17e2850)
source to share
My answer to this dilemma is as follows:
The existence of methods inside packages can be determined by statically checking its CODE reference as follows:
if( defined &Child::who ){
return "yes";
}
But since the parent can be overridden by many, many different children, and I don't want to access all of them manually, I will get the package reference (which will be the package name in string form) and evaluate it.
sub check {
my $self = shift;
my $package = ref $self; # 'Freak' or 'Child', etc.
my $is_overridden = eval "defined &".$package."::who";
die "eval error occurred" if $@;
return $is_overridden ? "yes" : "no";
}
And there you have it!
source to share