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

Hi,

I have a question regarding modules. I am learning Perl, slowly. For a project I am creating modules and classes, but something just doesn't feel right.

Currently, they're all interdependent. For example, one class handles sessions, a module contains misc subroutines, but each requires that the database class has been invoke so that they can call $main::db->query($sql);.

Something just feels wrong... is that the proper way of going about it? I have considered starting up the database individually in each module, but that would cause the server to do a lot of extra work, and would result in me doing extra work too if I changed the password or something.

What is the right way of creating classes and modules which "depend" on other classes and modules?
Thanks

Oh. Duh. I'd never really thought of passing along an object as a parameter. Is this an example of modules working "nicely" with each other? It just hit me while rereading your responses (and made me feel very stupid in the process :D !).
salazar: Question: If you pass an object as a parameter to a sub, can that sub access that object's properties and methods? e.g., my $obj = shift; $obj->active( time());, where active is a get-set method of $obj. Does this work?
tye: of course
salazar: -.-X

Replies are listed 'Best First'.
Re: How to write effective modules
by graff (Chancellor) on May 04, 2009 at 04:56 UTC
    You haven't provided much detail, so we're stuck with generalities... In general, if you find yourself creating a set of separate modules with dependencies between them (e.g. they all end up needing a database connection), it could be that you are dividing things up the wrong way. (Having a module that "contains misc subroutines" is probably a good sign that your partitioning of subs into modules may need some rethinking.)

    One way to refactor is to think about module division in terms of focusing the external resources: all functions that involve database interaction go together, all functions that involve i/o on a given type of data file go together, etc.

    Next, as you divide up the work into various objects and method calls (or simply into various modules and subs), you need to determine, for each method/sub, the particular data it needs (in the form of parameters provided by the caller, or settings established at start-up), and the particular range of results it delivers. The point of this step is to ensure some sort of independence for each method/sub, which in turn will yield some amount of stability and re-usability from a caller's point of view.

    It's not so bad if you end up with just "quasi-independence" -- e.g. if a file-i/o module has some methods that you think need to involve some database work, go ahead and set it up to expect a database handle as one of the calling params in this module's "new" method, or as a param to this or that method call. But if you think about it a little more, you can probably figure out how the caller can get the needed info from the database and pass that in the call, and/or how the module functions can return stuff that the caller would want to put into the database, so that you can keep the file-i/o module "pure" (no dependency on / contamination with database activity).

    The most important point is that hewing to this or that theory of software design is not the most important point. What matters (as another monk says daily in his signature) is that it works, and that it is maintainable.

Re: How to write effective modules
by trwww (Priest) on May 04, 2009 at 03:47 UTC

    Hello,

    I write web applications, often using either CGI::Application or Catalyst. What I've started doing recently is using CGI::Application as a framework for even my non web based apps.

    So then when I start asking myself questions like "boy, how am I going to cleanly get a database handle to everything that needs it?" I just hit up CPAN and find stuff like CGI::Application::Plugin::DBH. It usually comes out cleaner than anything I could come up with on my own.

    Regards,

      I recently started using CGI::Application, and it has really been a pleasure: very short learning curve, very clean concept of operation, and default behaviors that are cogent and really helpful to the application developer. The end result for me has been a great improvement over what I was doing before I started using this module, in terms of setting up, maintaining and adding to an app.
Re: How to write effective modules
by perrin (Chancellor) on May 04, 2009 at 04:42 UTC

    Is a global in the $main:: namespace the right way to do it? No, it is not! Sometimes you'll build a set of modules that all assume a certain runtime environment, but even then that environment will be encapsulated behind method calls (or at least subroutine calls), so other modules don't need to know the internals.

    Basic rule of thumb: it's fine for one module to depend on the public API of another module, but it's wrong to depend on the internal implementation of that API. And $main::db wouldn't be considered a good public API by most people.

      What is an example of, and how would you go about creating, a "good" public API?
        Template-Toolkit. The pieces depend on each other, but they all have good encapsulation.
Re: How to write effective modules
by lostjimmy (Chaplain) on May 04, 2009 at 03:33 UTC

    I don't know that there is really a "right way" to do it. If what you're doing is working and you're comfortable with it (although based on this post I have to assume you're not), then go with that.

    What I typically like to do to handle this situation is to pass the database class to the object during construction. So if you've got a session class, then maybe it would look something like my $session = new MySession($db), or better yet pass the parameters to the constructor as a hash reference: my $session = new MySession({db => $db, arg2 => $foo}).

    Looking into the future (maybe you'll want to change the database handle during execution...what do I know?), you could also add some accessors and mutators: $session->db retrieves the database object, and $session->set_db($new_db) changes the database the object is using.

Re: How to write effective modules
by ELISHEVA (Prior) on May 04, 2009 at 19:17 UTC

    Having one and only one piece of data that is shared by many modules is rarely a problem. In effect, that one-and-only-one is functioning as an "application object". However, most programs of any real size don't stay that way.

    The thing to watch out for is when you find multiple bits of data are always being passed together to functions. When that starts happening you need to encapsulate the things that get passed around together into a single object. Then instead of passing several parameters and having to remember what order you usually use to pass them, you can pass one single parameter that gives access to all the other information.

    The other thing to watch out for is things that change together. If setting one variable causes ripple effects in other variables, the thing that changes and all the variables affected by it should normally be encapsulated into a single object. Sometimes you may find that A triggers changes in B1,B2,B3 and B3 triggers changes in X,Y,Z. In that case you may need two objects:

    • Class1: encapsulates B3,X,Y,Z
    • Class2: encapsulates A,B1,B2, and a Class1 object

    Best, beth