Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Avoiding Globals with OO Perl

by Anonymous Monk
on Oct 20, 2011 at 18:50 UTC ( [id://932729]=perlquestion: print w/replies, xml ) Need Help??

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

Want to avoid using globals at all costs. Instead of passing variables from sub to sub decided to store variables needed for use throughout the program. Just wondering how bad the code is below. Perhaps it's ok but I'm not sure.

I create a basic object then later in the program add more variables to store then access later with getter and setter methods.

Opinions and better practices or ways would be much appreciated.

# Create Object my $obj = main->new( '1', 'STORAGE' ); my $string = 'Bad Code?'; # Store string in object $obj->setter( 'string', $string ); # Later on get string stored in object my $stored_string = $obj->getter('string'); sub new { my $class = shift; my $self = { _id => shift, _value=> shift, }; bless $self, $class; return $self; } sub setter { my ( $self, $set_name, $data ) = @_; $self->{"_$set_name"} = $data if defined($data); return $self->{"_$set_name"}; } sub getter { my ( $self, $get_name ) = @_; return $self->{"_$get_name"}; }

Replies are listed 'Best First'.
Re: Avoiding Globals with OO Perl
by CountZero (Bishop) on Oct 20, 2011 at 19:43 UTC
    There is nothing wrong per se with using global variables. It is just the way you use them that counts. If they are used to perform some spooky "action at a distance", then there is something wrong. But global variables used to set some parameters affecting the global working of your program is OK.

    I tend to store these global parameters in a hash I call %config and many times I load it from an external config file.

    Actually what you are doing is more or less the same, only you have gone one step further and use an object. So you just made it a bit more complex with no benefits to compensate for this complexity. Your way to get data in or out of your object is more difficult than using a hash. It does not protect you from any typos, since you do not even check in your getter method if that slot in your object ever existed beforehand and it is now being created silently (and perhaps wrongly).

    I guess your idea is to use this global object to move data in and out of your subroutines without having to use any variable as parameters. I can tell you that is the wrong approach. Subroutines should have a well-defined input and output interface, which should be obvious to any programmer, i.e. the first line of your sub should grab the data in the @_ variable and store it in lexical variables (with meaningful names) and all data returned by the sub should pass through a return statement. A sub should not "leach" or "leak" information in any other way.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

    </div

      I am learning alot thank you all.

      Want I want to avoid is this. Passing variables to subroutines that don't use them because subs inside need them

      #pass in vars some of which won't be used till deep #nested sub work($a,$b,$c,$d,$e,$f); sub work{ my ($a,$b,$c,$d,$e,$f) = @_; more_work($b,$c); print "$a\n"; } more_work{ my ($b,$c) = @_; even_more_work($c); print "$b\n"; } even_more_work{ $c = shift; print "$c\n"; }
        work($a,$b,$c,$d,$e,$f); sub work{ my ($a,$b,$c,$d,$e,$f) = @_; more_work($b,$c); print "$a\n"; } more_work{ my ($b,$c) = @_; even_more_work($c); print "$b\n"; }

        $d, $e, $f are never used, and $b & $c are only used by more_work().

        Then perhaps what you should be doing is:

        more_work( $b, $c ); work( $a )

        Or if work() requires the results of more_work(), then maybe:

        work( $a, more_work( $b, $c ) );

        I realise that your example was just that, but it demonstrates the problem with using made-up examples rather than real ones. It becomes all too easy to exaggerate the effect of the perceived problem -- that of passing lots of data around -- whilst obscuring what is often the real problem -- poorly structured code.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        That looks like a candidate for some light weight OO and named parameters. Consider:

        use strict; use warnings; my $obj = bless {flibble => 1, floop => 2, flop => 3}; $obj->work(nuts => 7, bolts => 5); sub work{ my ($self, %params) = @_; $self->more_work(%params); print "$params{nuts}\n" if $self->{flibble}; } sub more_work{ my ($self, %params) = @_; $self->even_more_work(%params); print "$params{bolts}\n" if $self->{floop} > 1; } sub even_more_work{ my ($self, %params) = @_; my $sum = $params{nuts} + $params{bolts}; print "$sum\n" if $self->{flop} == 3; }

        Although in real code a constructor would check important parameters and subs would check that any required named parameters are as expected.

        True laziness is hard work
        You know, you've faced the problem of my everyday erlang reality. There is no such thing as a global variables, but (sic!) there is a process-dictionary, the State and things alike. So, anyway, you need to have some dynamically modifiable in runtime params which you will need only on N-th in depth sub. Just create a hash-like structure and give a link to it in a bunch of your nested subs =)
Re: Avoiding Globals with OO Perl
by BrowserUk (Patriarch) on Oct 20, 2011 at 19:13 UTC
    Want to avoid using globals at all costs.

    Besides that "at all costs" is stupidly emotive and dogmatic, why?

    What benefits do you hope to accrue, or dangers do you hope to avoid, that you are prepared to expend "all costs" to achieve?

    With respect to your sample code, all you've done is trade an simple, efficient and obvious global string that you can pass to your subroutines and methods, for a complicated, slow and obfuscated global object that you pass, along with a global constant used to select the actual value from within the object.

    All in all, on the basis of your description, code and use-case, a completely futile exercise of creating unnecessary complexity and obfuscation in the name of some unachieved dogmatic ideal.

    You asked for opinions, and that is mine. Other's MMV.

    One of the "all costs":

    #! perl -slw use strict; use Benchmark qw[ cmpthese ]; sub new { my $class = shift; my $self = { _id => shift, _value=> shift, }; bless $self, $class; return $self; } sub setter { my ( $self, $set_name, $data ) = @_; $self->{"_$set_name"} = $data if defined($data); return $self->{"_$set_name"}; } sub getter { my ( $self, $get_name ) = @_; return $self->{"_$get_name"}; } sub test { $_ eq 'fred' } my $string; my $obj = main->new( '1', 'STORAGE' ); cmpthese -1, { a=> sub { for ( 1 .. 1000 ) { $obj->setter( 'string', "value$_" ); test( $obj->getter('string') ); } }, b=> sub { for( 1 .. 1000 ) { $string = "string$_"; test( $string ); } }, }; __END__ C:\test>junk44 Rate a b a 287/s -- -88% b 2437/s 748% --

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      One of the "all costs":

      What does that benchmark prove? In the unlikely case that an accessor is a bottleneck, consider hoisting an invariant out of a loop?


      Improve your skills with Modern Perl: the free book.

        There is no invariant in the loop.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Avoiding Globals with OO Perl
by eyepopslikeamosquito (Archbishop) on Oct 20, 2011 at 19:24 UTC

    You seem to have an XY Problem. If you give us more context of the type of system you are trying to design, we might be able to help.

Re: Avoiding Globals with OO Perl
by ~~David~~ (Hermit) on Oct 20, 2011 at 19:01 UTC
    Your object is now a global variable...

    You could create a package that houses all of your variables and share them:
    package Variables; use strict; use warnings; our $foo = 'Foo'; our $bar = 'Bar'; 1;
    And then in your main file:
    use Variables; use strict; use warnings; print $Variables::foo;
Re: Avoiding Globals with OO Perl
by sundialsvc4 (Abbot) on Oct 21, 2011 at 12:30 UTC

    To me, the main trouble with globals is merely an expression of a bigger core issue.   The bugaboo is (IMHO) really not so much that the data is in-effect scattered hither and yon, but that the logic is similarly scattered.   The latter is, IMHO, what you really want to avoid.

    A simple “getter/setter object” is a little better than nothing, because it does offer a centralized place at which to validate what an object contains ... but even that objective can be muddled if, as usual, “it depends.”   These are pure-design choices with no clear bright line rule.   I think you’d like to go a few steps further than that, if practicable, just to get more bang for your buck ...

    Here is what I think is a good road to take:   let your global data structures live in a singleton object, with getters and setters, “instantiate on-the-fly” capabilities, and so on ... whatever you decide you need ... and then try to minimize the number of other units that actually have to refer to the globals directly.   My analogy is:   “only the valet really knows where your car is parked, and only the cook really knows where the food is stored before it is delivered to your table.   And in both cases, only they need to care.”   The code that wants something, asks for it.   The code that fulfills that request knows about the globals singleton, and the globals singleton knows about that code.   Thus the number of packages that share code and/or data dependencies is thereby appropriately minimized.

Re: Avoiding Globals with OO Perl
by choroba (Cardinal) on Oct 21, 2011 at 10:41 UTC
Re: Avoiding Globals with OO Perl
by williams554 (Sexton) on Oct 21, 2011 at 05:36 UTC

    Have you seen perl critc?

    http://perlcritic.com/

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://932729]
Approved by BrowserUk
Front-paged by toolic
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (5)
As of 2024-04-25 14:36 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found