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

I am working on an OO module to support tracking the status of our various systems over time. My design is that I have a class HostGroup that contains a bunch of similar Host objects and a collection of event objects associated with them, a class Host that is intended to be subclassed to manage information particular to different types of hosts, and class Event which will be used to build up a history. I'd like HostGroup to handle persistance for all Event and Host objects, to simplify maintenance.

The problem comes up when I want to pull recreate a Host object from the persistant store. Each HostGroup has an attribute that specifies what subclass of Host to use to create the new instance, but how do I use or require the file based on a class attribute? Where is it best to do this sort of thing, in the constructor? How do use and require interact with scoping? Are repeated calls to either a problem?


TGI says moo

  • Comment on Inheritance and Container Classes and use/require

Replies are listed 'Best First'.
Re: Inheritance and Container Classes and use/require
by Masem (Monsignor) on Jan 03, 2002 at 02:08 UTC
    For how to use/require modules dynamically, see Best/possible ways to dynamically use modules. Once the module is loaded, it is retained in the appropriate namespace even if the use/require block goes out of scope: eg:
    use strict; use Data::Dumper; my $obj = do_sub(); print Dumper $obj; my $cgi = new CGI; print Dumper $cgi; sub do_sub { use CGI; return new CGI; }
    will result in no errors in the $cgi line.

    As for what you have, you probably want to implement the so-called 'factory' class that you don't instantiate from, but instead use 'static' methods that creates your various subclass instances given the class name in addition to any data that they need. In the perl implementation, this need not necessarily be a separate class, but it's easier to set up this functionality in this fashion if you're not used to it.

    -----------------------------------------------------
    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
    "I can see my house from here!"
    It's not what you know, but knowing how to find it if you don't know that's important

      Thanks for the great replies, everyone. Thanks to your help I was able to track down this node. Which goes into more detail on Factory classes.


      TGI says moo

Re: Inheritance and Container Classes and use/require
by theorbtwo (Prior) on Jan 03, 2002 at 01:56 UTC

    Well, multiple use and require aren't a problem... from perldoc -f require: "Note that the file will not be included twice under the same specified name. " So long as you use the same name, it isn't a problem. (Using different names would probably only come up with symlinks and suchlike.) Also, in general, evaling code that's mostly sub definitions isn't normaly a problem.

    If you want to have the effect of "use Foo;" at runtime, use "require Foo; Foo::import;", as perldoc -f use says.

    Thanks,
    James Mastros,
    Just Another Perl Scribe

      But importing at runtime is likely to be useless. The require should suffice. And OO modules shouldn't be exporting anything anyway!

      -- Randal L. Schwartz, Perl hacker

Re: Inheritance and Container Classes and use/require
by dragonchild (Archbishop) on Jan 03, 2002 at 02:01 UTC
    Hrmmm ... run-time compilation. *grins*

    My favorite answer would be to encapsulate this into some sort of Factory, pass the factory the value, and have it create the instance of the correct class.

    Of course, this is just punting the answer off to some factory class. How do you do it in the factory??

    if ($value eq 'A') { eval "use Blah::First"; } elsif ($value eq 'B') { eval "use Blah::Second"; } else { eval "use Blah::Default"; }
    Another, more extensible, possibility would be to do something like:
    my $basedir = "/some/base/dir/for/perl/Blah"; require "$basedir/$value"; # or --- eval "use Blah::$value";
    Now, require doesn't do an import() the way use does. Thus, if you have an import() anywhere in your hierarchy (typically if you use Exporter), then you have to make that function call yourself, maybe like "Blah::$value"->import(). That should work, but it is untested.

    I would recommend the first alternative, unless you feel really wild and free. The second is a little less secure and you have less control over it.

    Update: Changed to use eval, as per merlyn's comment.

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

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

      That's not a conditional "use". Those "use" are all executed at compile time, and you've ended up with an if/elsif ladder that does nothing at runtime!

      -- Randal L. Schwartz, Perl hacker