Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

jjohn (22)

jjohn
  (email not shown publicly)
http://taskboy.com/
AOL IM: taskboy3000 (Add Buddy, Send Message)

Perl hack/Linux buff/OSS junkie.

Journal of jjohn (22)

Thursday November 07, 2002
03:54 PM

A wonderful OO Gotcha

[ #8826 ]

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. ;-)

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
  • I don't know why you have the $b in there. Does every parent object wrap a child object?

    If you need a child object to create a parent, and can use a singleton, just lazily initialize it. The first pass will loop around a bit, but every pass after that, you're already done.

    --
    • Randal L. Schwartz
    • Stonehenge
    • I don't know why you have the $b in there. Does every parent object wrap a child object?

      The parent merely needs to use (and then discard) the child object. To make things a little more concrete, the Parent class (not it's real name) is there to provide both a reasonable object constructor for children and AUTOLOAD object properties accessor methods. For public methods, this class has a series of methods that return DBI handles, so that DB credentials are centralized in one place.

      Child classes provide

      • Ahh... I get it. You've got the wrong model. If

        Child->new

        might not return an object of type Child, then you're asking the wrong class. You just need a

        Parent->object_for_situation(@this)

        method, which has a lazy initialization for creating a specific Parent handle to help decide which Child_nnn class to create.

        There. That sorts it out. I'd be very confused if calling

        Child->new

        returned a Child2 instead of a Child. It doesn't make sense from an OO perspective.

        --
        • Randal L. Schwartz
        • Stonehenge