Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Generally accepted style in Perl objects

by Hot Pastrami (Monk)
on Dec 29, 2000 at 03:41 UTC ( [id://48723]=perlquestion: print w/replies, xml ) Need Help??

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

Good day.

I know that it is looked down upon to directly access the variables in an object with any OO programming language, including Perl. Is such still the case when the object in question is fairly complex?

For instance, let's say I have an object "User" whose constructor contains something vaguely like this (this is not a real sample of the code I'm working on, just a bit of mock code):
$self = { fname => "Hot", lname => "Pastrami", properties => { favoriteColors => [ "muave", "periwinkle" ], condiments => [ "mustard", "saurkraut" ], }, };
...would I be expected to write functions to access each of the values in $self->{properties}, or can I get away with assigning directly, such as:
my $user = User->new(); $user->{properties}{favoriteColors}[2] = "sepia";
I know that Perl allows both, but will directly assigning a value in an instance like this cause any self-respecting Perl developer to find me and make my dental records obolete?

Thanks,

Hot Pastrami

Replies are listed 'Best First'.
(Ovid) Re: Generally accepted style in Perl objects
by Ovid (Cardinal) on Dec 29, 2000 at 03:52 UTC
    One of the benefits of OO programming is abstracting the interface details from the user. Provide methods for them to update this information rather than allow them to mess directly with the object. What if you need to change the internals of the object? Then, all of the code that accesses it directly is in danger of being broken.

    I have one module that used to allow programmers to turn the modules function off and on. Internally, you could have done something like:

    $object->{ _on } = 0; # equivalent to the following: $object->off;
    However, as time went on, I realized that having this module's function "suppressed", with local overrides allowed, was superior to just "off" and "on." As a result, internally, the the "_on" key was no longer available. It was changed to "_supress", with potential values of 0, 1, and 2. However, for purposes of backwards compatibility, I still had the $object->off functionality available. No old code broke, but the module's internals were different.

    Cheers,
    Ovid

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re: Generally accepted style in Perl objects
by coreolyn (Parson) on Dec 29, 2000 at 03:53 UTC

    Well you're seriously wasting overhead programming in OO if you directly access the vars. As Damian Conway say's

    Thou shalt not access thy encapsulated data directly lest thou screwe it up.1

    By allowing yourself to take advantage of the casual nature of perl OO, you open yourself up to the problems that OO design seeks to avoid

    coreolyn


    1Object Oriented Perl pg. 82
Re: Generally accepted style in Perl objects
by stephen (Priest) on Dec 29, 2000 at 03:56 UTC
    The question is, if you make a change to the internals of one class, are you going to affect other classes?

    If you're messing with the internals of your class in three other modules, then you and other future developers will be sticking pins in voodoo dolls shaped like you if you have to make changes to the class in the future. :)

    Class::MethodMaker will let you create those methods without having to go through and type all those subroutines. This will also make them easier to change in the future.

    stephen

Re: Generally accepted style in Perl objects
by Hot Pastrami (Monk) on Dec 29, 2000 at 03:58 UTC
    That is what I gathered, but I thought I would find out what the general concensus was before moving forward. Laziness is what drove me to ask, I had little enthusiasm for the idea of writing a whole bunch of "addFavoriteColor()" and "removeCondiment()" subroutines. But, in the interest of doing it right, I'll hide away the specifics in subs.

    Thanks,

    Hot Pastrami
      One thing to consider: while you can use AUTOLOAD to create those methods for you, it has a lot of overhead. If those methods are to be called frequently, you can write them by hand *or* use AUTOLOAD and have it install the methods directly to the symbol table. Here's a (simplified) routine from Object-Oriented Perl by Damian Conway:
      sub AUTOLOAD { no strict 'refs'; my ($self, $newval) = @_; # was it a get_... method? if ( $AUTOLOAD =~ /.*::get(_\w+)/ ) { my $attr_name = $1; *{ AUTOLOAD } = sub { return $_[ 0 ]->{ $attr_name } }; return $self->{ $attr_name }; } # was it a set_... method? if ( $AUTOLOAD =~ /.*::set(_\w+)/ ) { my $attr_name = $1; *{ AUTOLOAD } = sub { $_[ 0 ]->{ $attr_name } = $_[ 1 ]; retur +n } }; $self->{ $attr_name } = $newval; return; } # Whups! Bad sub croak "No such method: $AUTOLOAD"; }
      That will auto-create "set" and "get" methods and install them in the symbol table for you. Subsequent calls to those methods will find them and not incur the AUTOLOAD overhead. Needless to say, this method is useful primarily if you have many subs with similar functionality.

      Incidentally, the "simplification" I mentioned was my removal of a hash lookup to verify whether or not one was allowed to read or update the variable in question. You'll want to account for that. I left it out so the code would be clearer.

      Cheers,
      Ovid

      Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

      You can always use AUTOLOAD to generate accessor methods on the fly: grab the name of the method and create (through eval) a method with that name that accesses the field in the object. If one day you want to change the implementation and write the method it will be found and AUTOLOAD won't be called anymore. CGI.pm uses this method, you should have a look at its code.

      It sounds like you may want to have multiple methods for your object's user to use, but may be able to get away with doing all the dirty work with only one method which you'd want to keep internal to your object. like:
      $obj->add_condiment('Cheeze');
      Then in your object's package:
      package whatever; ... sub add_condiment { my $obj = shift @_; $obj->set_property("add", "condiment", "cheeze"); } sub add_color { my $obj = shift @_; $obj->set_property("add", "favoriteColor", "blue"); } sub set_property { my $obj = shift @_; my $action_type = $_[0]; my $property_type = $_[1]; my $item = $_[2]; if( $action_type =~ m/add/i && exists $obj->{'properties'}->{"$prope +rty_type"} ) { push @{$obj->{'properties'}->{'$property_type'}}, $item; } elsif( $action_type =~ m/remove/i && exists $obj->{'properties'}->{" +$property_type"} ) { ## loop over the items till you find what you want to ## remove, then remove it } else { die "Something unplanned happened"; }
      Technically the larger method doing the dirty work could be called as a function(maybe more efficient) by explicity passing the object reference as the first arguement(i.e. <code>set_property($obj, "add", "condiment", "cheeze") ) Hope mantaining all those properties doesn't seem like such a daunting task now. :) toodlez.
      If the properties are completely unprocessed (i.e. you never rely on knowing what someone's favorite mustard is), you can probably get away with "add_property()", "set_property()", "get_property()", "get_all_properties()", and "delete_property()" routines. That'll keep you away from making assumptions about the internals that may later change, but is flexible enough so that you don't need to keep changing the class interface (which is only slightly better than having no interface at all.)

        I like the way you explained breaking out standard functions, from core functions. It's something I've always done, but have found myself hard pressed to enunciate as to why I do it. It has always stumbled out of my mouth as, "Cuz I don't want to mess the other files up".

        I do have a small point of contention with a portion of your conclusion though. You said,

        ...changing the class interface (which is only slightly better than having no interface at all.)

        Speaking from my position as a struggling Perl person that's learning both the base perl code and the many abstractions1 of perl as 00. I can suck the power of a 2 processor SPARC 450 because of the horrendous programing in my applications core modules. For this reason I contend that,
        Changing the class interface, is worse than no interface at all.

        coreolyn Duct tape devotee.
        -- That's OO perl, NOT uh-oh perl !-)


        1 From my slighlty crazed point of view, OO Perl takes Perl from being 'duct_tape' to 'silly putty on acid'.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (3)
As of 2024-04-24 21:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found