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

Hello Monks, I'm faced with what looks like a classical OOP problem: I have a bunch of modules which, by design, offer the same set of functions to the outside, but conceptually with different implementations. In practice, however, a large percentage of the modules have identical implementation for most functions. In OOP, I would therefore do it like this:

  1. Define a base class which implements defaults for all functions
  2. Derive my modules from the base and override only those few functions which are different (in most modules, this will be only one function)
Unfortunately I can not use this approach here (the modules are part of an existing framework and I can for example not make real objects out of them, i.e. no "bless" etc.). So I came up with the following solution:
  1. Define a "parent module" which contains default implementations for all functions
  2. In each module, I "use" the parent module. For the functions which are specific to the respective module, I write an implementation of the function. For those which are supposed to use the default, I use a forwarding function.
The forwarding functions look like this:
sub somefunc { &ParentModule::somefunc }
Since the majority of function definitions are forwarders, I tried to improve like this: I define a file "Includes.pl", which contains those most common forwarding definitions, and in those of my modules, which need these, I do a
require 'Includes.pl';
So far, this seems to work, but I'm not that happy with this solution. Maybe there is a more elegant way to do this? It is important, however, that an alternative solution does not involve invoking AUTOLOAD in my modules, because this would also cause problems in the current context. Any ideas?

-- 
Ronald Fischer <ynnor@mm.st>

Replies are listed 'Best First'.
Re: Forwarding functions to a different module
by Corion (Patriarch) on Aug 07, 2008 at 10:35 UTC

    Personally, I would just set up aliases to the functions, and wrap that functionality in a bit of code. For example:

    package ParentModule; use strict; sub base_foo {}; sub base_baz {}; sub import { my ($class,@routines) = @_; my $target = caller; # Install the requested routines in the target package for my $name (@routines) { no strict 'refs'; *{"$target\::$name"} = \&{"base_$name"}; }; }; 1;

    And then in the child classes:

    package ChildModule1; use strict; use ParentModule qw( foo bar ); package ChildModule2; use strict; use ParentModule qw( foo ); # Own implementation of bar() sub bar {...}; 1;

    You can get fancier and provide hashes for name mappings.

      This is great! I have never attempted to write my own import routine, so this is also of high educative value....

      -- 
      Ronald Fischer <ynnor@mm.st>
Re: Forwarding functions to a different module
by Tanktalus (Canon) on Aug 07, 2008 at 22:15 UTC

    Put your common functions in a module. That module will inherit from Exporter. It will also set up everything for EXPORT_OK, or whatever as you'll find appropriate. Each other module can just use Common qw(funcs this module does the default action for);. That's exactly what you're asking for ... and the only difference between this and Corion's answer is that this one uses a standard module to do the dirty work (playing with 'no strict' and stuff).

Re: Forwarding functions to a different module
by tod222 (Pilgrim) on Aug 07, 2008 at 19:27 UTC
    Can't @ISA be used here?

    From perlmod:

    a package may also derive some of its methods from another class (package) by listing the other package name(s) in its global @ISA array (which must be a package global, not a lexical).
    By using @ISA, no forwarding functions are needed if the methods are the same, so it's more efficient. It's also the standard practice. Is there some reason @ISA doesn't work in this situation?

    Note: I'm no expert and don't know whether using @ISA invokes the AutoLoader, which you seek to avoid, but were I in your shoes I'd be doing inheritance in the standard way using @ISA and then dealing with any AutoLoader issues.

    Update: I'm not clear on your constraint not to use 'bless', but the @ISA behavior works without bless. Note that the example in 'perlmod' is introduced as 'a traditional, non-OO module called Some::Module', yet it includes

    @ISA = qw(Exporter);
    (Updated again to ask about ISA working in this situation.)
      I'm not clear on your constraint not to use 'bless', but the @ISA behavior works without bless.

      This was my misunderstanding. I thought @ISA works only when a function is called indirectly via a blessed reference! I will try out whether your suggestion works also in my context (the difficulty is that my module will be used as mixin together with other modules, in order to form one big class, using Class::MixinFactory, and I don't know yet to what extent this effects package lookup (maybe it would be a good idea to study MixinFactory a bit).

      -- 
      Ronald Fischer <ynnor@mm.st>
        I thought @ISA works only when a function is called indirectly via a blessed reference!

        You were close. It only works for method dispatch. (Constructor calls use method dispatch even though they lack references of any kind, blessed or otherwise.)

        The A Class is Simply a Package section of perlobj explains how @ISA and inheritance work. I should have linked it in my earlier post.

        I'd recommend reviewing all of perlobj, not just the section mentioned above.