Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Scope of a Module?

by Masem (Monsignor)
on Jul 07, 2001 at 03:34 UTC ( [id://94652]=perlmeditation: print w/replies, xml ) Need Help??

One of the key concepts of programming that I've read is to try to keep classes and the like focused; try to keep interfaces simple and don't fit too many things into one class that probably won't be needed by more than half your audience.

In particular, I've recently gotten a comment back on my DBI::Pretty module that suggests going beyond this. What I have currently is that the user inputs the SQL using a straightword hash, a keyword and the SQL as key and value, respectively. However, the comment suggested an number of other ways to enter this data, such as from a file, from XML, or other ways. These are certainly not unreasonable in terms of their usability for the intended use of the class, however, I'm concerned with the fact that 1) these will increase the size of the class and 2) the user can 'easily' preparse the needed hash from these items themselves.

Now, I'm thinking that it might be possible to create subModules (DBI::Pretty::ModNameHere), which are only loaded if what the user passes it is anything but a hash but meets the needed requirements, eg:

if ( $arg is a FILEHANDLE ) { use DBI::Pretty::File; %hash = read_stuff_from_file( $arg ); } elsif ( $arg is a XML datastream ) { use DBI::Pretty::XML; ... } etc.
Though I'm not sure of the usability of 'use' in the blocks, and will this code be around after the block is removed. But the key thing here is that if one uses just the needed hash, no extra code would be needed, only in cases of other methods would it be loaded. But again, in this case, is it not already reasonable that the user can get the data in the format as needed?


Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain

Replies are listed 'Best First'.
Re: Scope of a Module?
by frag (Hermit) on Jul 07, 2001 at 05:54 UTC

    It seems like what you're trying to do violates modularity in a way that'll cause problems. That is, somebody who wants to extend this to a new format should ideally be able to just whip up a DBI::Pretty::Foo and start using it in scripts. But with this design, any such developer also has to muck with the DBI::Pretty.pm as well, adding an elsif to that bit of (pseudo-)code. This is really undesirable, potentially making it difficult to maintain and use your module.

    As an alternative, how about this:

    • Instead of 'my $hash = shift || {}' (inside your constructor), create a parse_input() method. (Or grab_input(), or something similar.) In the case of your existing module, it would just consist of that one statement, returning a hashref.
    • Subclasses handling different forms of input could now be created by simply replacing the new() and parse_input() methods with ones of their own construction, returning a hashref that conforms to your requirements. (Actually, you could make it so that people extending your module would only have to rewrite parse_input(), if you get creative with the way you write your new() method -- blessing an object immediately, and making all additional calls in new() through the -> operator, i.e. "$self->parse_input".)
    • Anyone using the module must now decide in advance what form the input will take, and must explicitly choose the corresponding module ('use DBI::Pretty::XML').

    If you do want the module to be flexible enough to handle a few alternative situations by default without requiring subclasses, just place that if/elsif you've got inside the default parse_input(), but rather than calling alternative use statements, each condition should just invoke different parse_foo_input() methods. Doing it like this -- in parse_input(), not in new() -- shouldn't interfere with the ability for other people to roll their own subclasses.

    But the simplest/best thing is probably to just require that anyone using the module must themselves pick the subclass(es) that they want. You can then provide a few basic alternative subclasses along with the main module.

    -- Frag.

Re: Scope of a Module?
by John M. Dlugosz (Monsignor) on Jul 07, 2001 at 05:27 UTC
    I like the idea of only bringing in parts as needed. I have a script that converts A to B and B to A, and they are largely separate code. But the user sees one script with common arguments, and it loads only the code for the one it needs when invoked.

    use will not work that way, because it is evaluated at compile time and the if at run time. However, you can do the same thing as use by calling require followed by import. For object-oriented modules, the import may not be needed anyway.

    Now, as to the scope: If you don't call import at all and it works OK, then you have no issues. The module remains loaded so objets of that class continue to work, and you can refer to DBI::Pretty::File as a class name or scope name subsequently and it's still there. Subsequent require's are harmless, so you could make sure it's in at each point you name it directly.

    If the module's import routine "does something", there are two things it can do: it can manipulate the current block scope like use warnings; does. It does this by modifying certain magic variables that are like local in that they pop back to their old value when the user's scope is done.

    Normally, non-pragmatic modules "do things" in the import by modifying the symbol table of the caller. That is, add symbols to the caller's package. This will alter your namespace and is totally insensitive to block scope.

    I hope that helps,
    —John

      Maybe autouse is the simplist way to do this.

        p

Re: Scope of a Module?
by Abigail (Deacon) on Jul 10, 2001 at 01:40 UTC
    You cannot do use Module and assume it to have only lexical effects. use is handled at compile time. You can do what you want at run time though, by using require. Something like:
    if ($arg is a FILEHANDLE) { require DBI::Pretty::File; %hash = DBI::Pretty::File::read_stuff_from_file ($arg); } elsif ($arg is a XML datastream) { require DBI::Pretty::XML; %hash = DBI::Pretty::XML::read_stuff_from_xml ($arg); }

    I do get the feeling you are mixing OOP with procedurial coding and getting the worst of two worlds. I would either make $arg an object so I could do $arg -> read_stuff, or I'd make a function, possible in a separate file (and module), so that I could do %hash = read_stuff ($arg) and put all the reading stuff from DBI::Pretty::File and DBI::Pretty::XML and other modules in there.

    -- Abigail

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://94652]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (6)
As of 2024-03-19 08:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found