A wonderful OO Gotcha

Posted:

I like object-oriented programming. It can really simplify develpment when used judiciously. OOP class hierarchy are best kept simple and linear. Here’s some fictionalized code that captures the essence of a bug the caused the Linux server it was running on to lock up so tightly, it needed to be cold booted.

#

File: Parent.pm

#

package Parent;

sub new { my $proto = shift; my $class = (ref $proto || $proto);

if (my $r = Apache->request) { require Child; $b = Child->new; }

return bless {}, $class; } END

#

File: Child.pm

#

package Child; @ISA = qw(Parent);

sub new { # some class inits go here return shift->SUPER::new; } END

#

File: caller.pl

#

use Parent; $b = Parent->new;

The caller.pl script is called under mod_perl, but that’s not particularly important (although the server crash was due to runaway Apache processes caused by my coding gaff above). Can you see why this code is likely to take a long, long, long time to run?

It’s almost a while(1) loop.

The Parent defines a constructor called new(). The child overrides that that constructor, but still calls the parent constructor for some initialization. Today, I added a hack to the Parent class that required an object of the Child class to be constructed in the Parent’s constructor. When caller.pl tried make a new Parent object, the Parent needed a new Child object, which calls the Parent’s constructor, who in turn calls the Child’s constructor, who in turn calls the Parent’s constructor, who in turn…

Well, you get the picture.

The unsatisfactory (but effective) hack I came up with to break this cycle is in the Parent’s new:

#

File: Parent.pm

#

package Parent;

sub new { my $proto = shift; my $class = (ref $proto || $proto);

if (my $r = Apache->request && $class eq ‘Parent’) { require Child; $b = Child->new; }

return bless {}, $class; } END

If anyone (like say Damian Conway) would like to suggest a more polite way of doing this, I’m all ears. No, I can’t move the Child code I need into Parent because that would be like grafting a second evil head onto my shoulders.

I share my errors so that you may avoid them in your lives. ;-)

[Original use.perl.org post and comments.]