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

Fellow Monasterians,

I was reading an old node called What shortcuts can I use for linking to other information? and it raised more questions.

In my earlier days as a Perl coder I used what perlmod calls a "tradition non-OO module" to share code. I never understood all the "EXPORTing" stuff, and all the typing of subroutine names was tedious, but the approach worked fine.

For the past few years I've used CGI::Application exclusively for all my web applications (95% of what I use Perl for), and have been using a "simplified" approach of use base qw(SomeModule), which works well and is easier on the wrist.

My question is simply, are there any caveats/ramifications to abandoning the non-OO method and exclusively using the "OO-inherited-class" module (for want of a better name)? Pluses? Minuses?

Some simplified examples:

PREVIOUSLY USED METHOD

#common routines to share among all modules package Common; use strict; use Exporter; use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); *import= \&Exporter::import; @EXPORT = (); @EXPORT_OK = qw(get_today get_setup); %EXPORT_TAGS = ( All => [qw(&get_today &get_setup)]); sub get_today { #get date } sub get_setup { #get application parameters } 1; #example of a calling module package SomeFile1; use strict; use Common qw(:All); #...CGI::Application setup, etc... sub initialize { my $self = shift; my $day = $self->get_today(); my $setup = $self->get_setup(); } 1;

NEWER METHOD

#code to share package Super; use strict; use base 'CGI::Application'; sub get_today { #get date } sub get_setup { #get application parameters } 1; #example of calling module package SomeFile1; use strict; use base 'Super'; #...CGI::Application setup, etc... sub initialize { my $self = shift; my $day = $self->get_today(); my $setup = $self->get_setup(); } 1;

Thanks in advance


—Brad
"The important work of moving the world forward does not wait to be done by perfect men." George Eliot

Replies are listed 'Best First'.
Re: Questions about sharing code
by perrin (Chancellor) on Nov 27, 2007 at 14:21 UTC
    In general, I prefer not to import or export anything. I find it confusing when I look at the code later, and I don't mind verbose function calls when they give me extra documentation about where things came from.

    However, inheritance may not be the right choice for sharing OO code. Only use inheritance as a way to model behavior of an object, not as a way to simply share code. If you just need to share some code but the behaviors in it don't make sense as an inherent behavior of the object you want to use them from, rethink your object model and figure out what concepts are not being modeled. You may need a new class, which the other classes call or delegate to.

    Sometimes I even resort to the dreaded "Foo::Util" class for things that don't fit nicely anywhere else. It's still better than using inheritance just for access to some methods.

Re: Questions about sharing code
by dragonchild (Archbishop) on Nov 27, 2007 at 15:27 UTC
    There are a few modules on CPAN that I use over and over and over. Every single one of them exports a set of functions.
    • Scalar::Util
    • List::Util
    • List::MoreUtils
    • File::Temp
    • File::Path
    • FindBin

    The one module that I should use more that I don't (and won't unless forced to) is File::Spec. I HATE the pseudo-OO interface with an absolute passion. Oh, I understand why it was written that way and I'm not sure I would've written it differently under the hood. But, the UI just sucks so badly that it hurts.

    There are also a few modules on CPAN that I use a lot that provide an OO interface. This is because there is state information that needs to be preserved.

    • DBI
    • CGI

    Yes - CGI is OO. The functional interface is an absolutely horrid and I refuse to use it.

    The point is that you should provide a functional interface if they are side-effect-free functions. If there is data that needs to be preserved across function calls, then you should provide an object.


    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?

      The one module that I should use more that I don't (and won't unless forced to) is File::Spec.

      Have you looked at File::Spec::Functions?

        Yes, I have, but that's not a good example of an egregious interface. And, it begs the question "Why isn't that the default interface?"

        My criteria for good software:
        1. Does it work?
        2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?

      Thanks dragonchild for the examples. It really helps to see what others are doing.

      So, what I'm hearing is: unless what I am doing is specifically OO or needing inheritance, I should just rely on the non-OO-Export method.

      Correct?


      —Brad
      "The important work of moving the world forward does not wait to be done by perfect men." George Eliot
        Use the right abstraction for the job. OO is a great pattern to work in, but it has a cost associated with it. Use it when necessary.

        Here's another way to look at it - C doesn't have any OO built into the language (leaving aside C++ for the moment). Yet, most stuff is written in C. Yeah, some of it could benefit from OO, but how well would math.h or stdio.h really benefit from it? Those are state-free functions. OO is for tagging state with behavior. If you have no state, then why OO?


        My criteria for good software:
        1. Does it work?
        2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?

      Not to flog a dead horse...

      What about:

      #tapping a locally placed file of subroutines use Mycommonroutines;
      OR
      #using the lib command to change the @INC use lib Mycommonroutines;
      OR
      #using Export to be more specific about what subs to import use Mycommonroutines qw(get_setup);

      Can I assume:
      1. use of lib in this case is ill-advised?
      2. the advantage (only?) of the Export route is to allow specificity of what routines are imported?


      —Brad
      "The important work of moving the world forward does not wait to be done by perfect men." George Eliot
        First off, your second option doesn't compile.

        As for the difference between the first and the third, you need to seriously look at maintenance for the answer. For all the code in a given file, you can do a search for "sub foo" and find out what foo does. But, if it's a subroutine from another module, you can't easily find it. If there's just one Mycommonroutines, then you know where it is. But, what if there are 2? 5? 10? It gets a little hairier.

        It is for this reason that I and other CPAN authors have started using the following meme:

        # Explicitly state nothing will be imported use Scalar::Util (); # Call the function with the complete name Scalar::Util::weaken( $some_ref );

        For functions like weaken(), this may seem like overkill. But, this kind of habit will help maintainers because it becomes a flag to say "I didn't write this code in this file and this is where you can find it." And, conversely, if it doesn't have a prepended location, then it must be in this file somewhere. Any kind of hint like that will help your maintainer. Remember - code as if the guy who sees you code after you knows where you live and likes to swing axes.


        My criteria for good software:
        1. Does it work?
        2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: Questions about sharing code
by moritz (Cardinal) on Nov 27, 2007 at 14:34 UTC
    I don't really understand why you want to change from procedural to OO interface. If you don't have a good reason to switch, don't.

    The general advise is to use a procedural interface when the functions in a module don't work on the same data.

    If you have many subs sub1, sub2, ... in your module, and you call them all like sub1($data, @other_args); sub2($data, @other_args); it might be high time to pull $data into an object.

    If not, simply stay where you are.

Re: Questions about sharing code
by Your Mother (Archbishop) on Nov 27, 2007 at 23:18 UTC

    I'm gonna agree with perrin (typical!) and disagree, partially, with dragonchild (WTF OMG).

    For straight function code I've taken to doing things like-

    use HTML::Entities (); ... HTML::Entities::encode_entities($whatever);

    Because I can't stand reading code later that is hard to follow or potentially sends you to the wrong package with much confusion. With cutting+pasting and knowing your IDE, fully qualifying stuff is easy to type and much easier to follow later.

    For OO, the name space is carried in the object so, no problems.

    Also, I find using CGI.pm's OO interface goofy. The main non-aesthetic difference is that your code will run slower as OO. Otherwise, CGI::param() is no different from $cgi->param(). It's great to have CGI.pm's objects when you need to sling objects around (like in TT2 or something), but many, most I've written, CGIs are standalone in a way that makes imperative code easier to read and much easier to type.

    use CGI; my $cgi= CGI->new(); print $cgi->header(); # etc...