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

I am trying to manage a situation in which several perl modules may have the same package name. That is, imagine the following situation. I have a module Foo defined like:

package Foo; sub new { ... }; sub xyzzy { ... }; 1;
This module is then used in several places throughout the application as:

require Foo; my $foo = new Foo ( ); print $foo->xyzzy ( );
Now I would like to subclass Foo but still use the class name Foo. That is, I don't want to subclass Foo as, say, Bar because this would require changes to all the references to Foo throughout the system - something that is undesirable for those implementations in which I do not want to subclass Foo. (Imagine Foo as being a system module used by other system modules.)

So, to do this I moved the original Foo to a directory named System and created a new Foo in a directory named Custom as follows:

# The new Foo. package Foo; require System::Foo; @ISA = qw ( System::Foo ); ... 1;
And then set @INC to be:
With this, "require System::Foo" (needed in the subclass) will work as will "require Foo" which will find the custom Foo module if it exists and will find the system Foo module if it doesn't.

So far so good. I can get the system to at least find my custom modules before the system modules. This example doesn't work though unless I change the namespace of the system Foo by changing the package call. That is, I have to change the definition of the system module as follows:

package System::Foo; ...
So I'm willing to do this to the system objects but there's still a problem with those modules that do not have a custom subclass. That is, now all the places where I used the system module Bar as "require Bar" or "new Bar" have to be changed to "require System::Bar" and "new System::Bar." Well, that doesn't help at all as when I want to subclass Bar I would have to change these all back again.

I've tried various things like using a variable for the package name but perl doesn't like that. (I take it "package" is a compile-time command and not a run-time one.) I also tried moving the %Foo:: namespace to %System::Foo:: after loading the module but this failed because of a circular require path though I might try this route again unless I hear a better solution.

The only other solution I can think of is to write stub modules for all the system modules I might like to subclass someday. These stubs would subclass (in an identity relationship) the system modules but would reserve the namespace Foo for future changes. That is, the system modules would be named System::Foo but would only ever be accessed through the stub modules and so appear to be named straight-up Foo. Then I would change my installer to overwrite the stub modules as needed. This seems really ugly.

But then, is what I'm trying to do really ugly? Perhaps. I'm pretty sure the same problem exists in C++ and Java - maybe I'm just spoiled by the openness and flexibility of perl in so many other situations.

Thanks for any help or ideas,
Mike

Edit kudra, 2002-06-06 s/pre/code/, added readmore

Replies are listed 'Best First'.
Re: package namespaces
by IlyaM (Parson) on Jun 04, 2002 at 21:53 UTC
    Typical solution for your problem is factory pattern. You don't have to play these dirty games with namespaces. Just create one more package:

    package Bar; use Foo::XXX; sub create_foo { return Foo::XXX->new(@_); }

    Idea is that you don't call constructors directly (i.e. Foo::XXX->new) but delegate it to third party (i.e package Bar). This way you don't have to put name of Foo:XXX package in your code. You can always create its instancies via Bar package:

    use Bar; my $foo = Bar->create_foo;

    If at some moment you decide to start using package Foo::YYY->new instead of Foo::XXX->new than the only place you have to modify is Bar::create_foo().

    --
    Ilya Martynov (http://martynov.org/)

      Excellent. Thanks so much!
Re: package namespaces
by perrin (Chancellor) on Jun 04, 2002 at 22:43 UTC
    I don't understand why your calls to Bar have to change to System::Bar. Why don't you just leave Bar as it is?
      Because I would like to generalize the solution across all of the modules that I call "system" modules. The group of modules make up an application or toolkit that I would like to be able to customize on a per client basis while maintaining the core code base. Therefore, also on a per client basis, I would like to be able to subclass any of the system's modules without rewriting them each time. This means that whatever I do to make Foo subclass-able, I need to also do to Bar.
        The point of all this OO stuff is better abstraction. If you are always calling Bar with methods (either class methods or object methods) you can completely change the implementation of Bar at any time. You can make it subclass from System::Bar, or Quux, or System::Quux, or all of them at once. All that matters is that you keep the public interface for Bar the same. You will never need to change code that calls methods on Bar unless you change the interface of Bar.