Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

Re: A class that cannot be subclassed.

by dynamo (Chaplain)
on Feb 27, 2008 at 22:15 UTC ( [id://670771] : note . print w/replies, xml ) Need Help??

in reply to A class that cannot be subclassed.

Well, as far as making it impossible to subclass, no, I don't think that's doable. However making things difficult is easy. (isn't it always?) Suppose you have a method selfcheck, that checks the species of itself before running each other method (and at destruction):
package Foo::Bar; my @lineage = ('Mom', 'Dad'); @ISA = (@lineage); sub selfcheck { die "You can\'t change who I am." unless __PACKAGE__ eq 'Foo::Bar'; map { die "You can\'t change who my parents are." unless $lineage[$_] eq + $ISA[$_] } (0..(scalar @ISA - 1)); } sub DESTROY { $self->Foo::Bar::selfcheck; } sub method_name { my $self=shift; $self->Foo::Bar::selfcheck; # method stuff here } sub method_name2 { $self->Foo::Bar::selfcheck; # method stuff here }
Now, you can overload all methods in the child class, but if you do you aren't really subclassing. You might as well subclass an anonymous hash. If you don't cover all bases, then the first time an inherited method is called, the program will die uglily and point out to the foolish programmer the error of his ways.

- d

Replies are listed 'Best First'.
Re^2: A class that cannot be subclassed.
by kyle (Abbot) on Feb 27, 2008 at 22:53 UTC

    I think the initial test in selfcheck should be:

    sub selfcheck { my $self = shift; die "..." if ref $self ne __PACKAGE__; # lineage check... }

    As you have it, the test would always pass because __PACKAGE__ is the package that the sub was compiled in (Foo::Bar) regardless of anything else (see perlmod).

    If I were a "malicious subclasser" faced with this, I could redefine selfcheck (I think), and then it's smooth sailing. What you could do is put that in a lexical subref, then it would stay private.

    package Foo::Bar; # ... my $selfcheck = sub { my $self = shift; die ... }; sub method { my $self = shift; $selfcheck->( $self ); # ... }

    I'm not sure where to go from there. Putting that check seems like a lot of work for the goal, but I guess it could be worse.