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

Hey all, I often run into this situation in OOP. A base class implements a method, such as:
sub X { my $self = shift @_; ... $self->_Private_X(); ... }
Where method X() handles most of some functionality, but i want children to be able to override part of the behaviour with _Private_X(). But the base class often doesnt need a _Private_X().

So, i see 2 different (clean) ways to do this, one where this base class has a 'dummy' _Private_X(), which doesnt do anything, and the other is by modifying the X() method as follows:
sub X { my $self = shift @_; ... if ( $self->can('_Private_X') ) { $self->_Private_X(); } ... }
Either way does the job, today im leaning towards the can() way of doing things.

Does anyone have any particular reasons to do this either way? Or some other way?

Replies are listed 'Best First'.
Re: overridden method - best way
by dragonchild (Archbishop) on May 25, 2004 at 17:26 UTC
    I have the base class provide a dummy method. This does two things:
    1. It documents the interface in the base class
    2. It keeps you from messing up when you forget to call can().

    It's also faster, to boot. And, it allows for expansion later on down the road, in case you do want everyone to have a default _Private_X() behavior.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

    I shouldn't have to say this, but any code, unless otherwise stated, is untested

Re: overridden method - best way
by adrianh (Chancellor) on May 25, 2004 at 18:57 UTC
    Does anyone have any particular reasons to do this either way?

    I'd lean towards the 'dummy' method approach for a few reasons:

    • I try and make the 'normal' control flow of a method be as obvious as possible (illuminate the mainline). Having that if-statement there makes the code little bit harder to parse, and most of the time it will be unnecessary.
    • If I need to ensure that sub-classes override _Private_X I can make the base-class's implementation die and get a runtime error if I forget to override it in the subclass
    • If I later decide that I need some _Private_X behaviour in the base class I have somewhere to put it.
    • Including the method present in the base class makes the interface of the class easier for the casual reader to understand.

    I'd also not use the leading underscore convention for this method. Since it's intended to be overridden in sub-classes I'd look upon it as part of the public API rather than something private.

    Or some other way?

    Depending on the application it might make sense to delegate the _Private_X behaviour to another subroutine or object. For example if you wanted different _Private_X behaviour on a per-object rather than a per-class basis, or if the behaviour was conceptually separate and might be reused in different sub-classes.

Re: overridden method - best way
by geekgrrl (Pilgrim) on May 25, 2004 at 17:45 UTC
    I would have the method X() in the children as well, and call SUPER from there if you want to run the parent method.
    package MyChild; sub X { my $self = shift; $self->SUPER::X; # do stuff particular to the child }
      I would have the method X() in the children as well, and call SUPER from there if you want to run the parent method.

      The problem with this (and jaa's response) is that it solves a different problem from the OP's :-)

      When you specialise a method through inheritance you can either replace it completely, or add behaviour before and after it runs.

      However with the OP's method:

      sub X {    my $self = shift @_;    ...    $self->_Private_X();    ... }

      the behaviour that we want to tweak occurs in the middle of X's execution. You can't get at that by specialising X in a subclass.

      You see this pattern quite a lot when you have the same skeleton behaviour with various different implementations. The OP is doing exactly the right thing in isolating this behaviour that changes in a separate method that can then be specialised in each subclass.

        so why not make _Private_X a dummy method in the base class, and implement it in the children. Or am i not getting this thread?
Re: overridden method - best way
by jaa (Friar) on May 25, 2004 at 17:50 UTC

    Your base class should not really have to guess all the future ways in which derived child classes can screw things up :) in the base class, implement the method you want:

    package Parent; sub X { my $self = shift @_; print "called Parent::X()\n"; ... } # ------------------- # and in the Child class you specialise this package Child; use base qw(Parent) sub X { my $self = shift @_; $self->SUPER::X(@_); # Call Base class method # then do some extra stuff print "called Child::X()\n"; ... } # ------------------- # and further down the chain... package GrandChild; use base qw(Child) sub X { my $self = shift @_; $self->SUPER::X(@_); # Call up one level # then do some extra grandchild stuff print "called GrandChild::X()\n"; ... }
    This works to any depth of inheritance. One of the main benefits of OO is this type of inheritance where you can specialise a child class without having to recode the Parent and causing unanticipated issues elsewhere.

    So in my opinion, _Private_X should be court marshalled. I usually find that $obj->can('method') is more suited for use in handling collections of different types of objects. If you are using it for an inheritance hierarchy trick, look again - its probably a mistake.

    Regards,

    Jeff

      Oh - and a little trick I use to prevent private methods from fooling the inheritance chain...
      package Parent; sub X { my $self = shift @_; print "called Parent::X()\n"; ... } # ------------------- # and in the Child class you specialise this package Child; use base qw(Parent) sub X { my $self = shift @_; $self->SUPER::X(@_); # Call Base class method # local function NOT inherited _X($self,@_); ... } sub _X { my $self = shift; print "called Child::_X()\n"; } # ------------------- # and further down the chain... package GrandChild; use base qw(Child) sub X { my $self = shift @_; $self->SUPER::X(@_); # Call up one level # then do some extra grandchild stuff # local function NOT inherited _X($self,@_); ... } sub _X { my $self = shift; print "called GrandChild::X()\n"; }

      Note that I do not want the _X() subs called through the inheritance hierarchy, so I call them directly as functions in the current package.

      Regards,

      Jeff

Re: overridden method - best way
by dcvr69 (Beadle) on May 25, 2004 at 21:14 UTC

    I asked almost the same question in Inherit or copy-n-paste? but didn't get quite as many responses. Maybe it was my choice of node title. :)

    I like your dummy method option. I chose to solve my particular problem with a callback, but I didn't like that method, because I had still had to copy the arg handling portion from the base class in order to insert the callback into the passed arguments. It works, but still feels like a kludge. I shouldn't have to know about the internals of the base method in order to override/extend it.

    I'll have to try the dummy method technique, and see if it simplifies the interface/implementation a bit.

    dcvr69