Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Private methods

by crouchingpenguin (Priest)
on Mar 09, 2003 at 17:08 UTC ( #241580=perlquestion: print w/ replies, xml ) Need Help??
crouchingpenguin has asked for the wisdom of the Perl Monks concerning the following question:

After seeing characterstics of private in perl it reminded me of a project I had been working on where I had used the following to create a private like method:

package Foo; use strict; use warnings; my $_init_ = sub { my $self = shift; print STDERR ref($self) . "::_init_ called\n"; }; sub new { my $type = shift; my $class = ref($type) || $type; my $self = { attribute1 => undef, attribute2 => undef, }; bless $self,$class; $self->$_init_(); return $self; } 1;

And then the usage went like this:

#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use lib "./"; use Foo; my $foo = new Foo(); print Dumper(\$foo),"\n";

And I was able to subclass as needed, such as:

package Bar; use strict; use warnings; use base qw( Foo ); 1;

With the useage similar to the Foo usage:

#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use lib "./"; use Bar; my $bar = new Bar(); print Dumper(\$bar),"\n";

My question is, will this cause problems down the line when the application as a whole gets really complex? Is this safe and if not, why?

cp
---
"Never be afraid to try something new. Remember, amateurs built the ark. Professionals built the Titanic."

Comment on Private methods
Select or Download Code
Re: Private methods
by Ovid (Cardinal) on Mar 09, 2003 at 18:30 UTC

    Whether or not this code will cause problems depends upon how annoying the programmer finds the constructor's side effect of writing information to STDERR. Personally, I would not want that. However, I want to take a moment to go over your code because I see a few things that I might do differently. This isn't an answer to your question, per se, but more of a meditation regarding general coding style that might provoke interesting discussion.

    01: my $_init_ = sub { 02: my $self = shift; 03: print STDERR ref($self) . "::_init_ called\n"; 04: }; 05: 06: sub new { 07: my $type = shift; 08: my $class = ref($type) || $type; 09: my $self = { 10: attribute1 => undef, 11: attribute2 => undef, 12: }; 13: bless $self,$class; 14: $self->$_init_(); 15: return $self; 16: }

    I don't have a problem with you wanting to use a private method, but I disagree with how you're using it (I think the printing to STDERR was merely to show us how this works and not a programming technique you actually use, so I'll skip that). Instead, I'm more concerned with how you are using $_init_. Generally, I would see something like the above written as:

    sub new { my $class = shift; my $self = bless {}, $class; $self->_init; } sub _init { # instance initialization }

    If you have specific initialization for the instance of an object, it goes in _init and class initialization would be in new. (Many programmers put all of the initialization in _init() so that new() creates an object and _init() initializes it.) With that, subclasses can inherit new, get the appropriate class information and override _init to provide instance information appropriate for their class. However, this means overriding a private method!

    This seems to be a highly contested issue with Perl OO programmers. Most feel that private methods should remain private and only the public interface should be exposed (which I now tend to agree with). Others seem to feel that you can override anything you want. If you need to get something done, sometimes the best way is to break the rules.

    In the case of your code, you have an _init method that isn't initializing anything and can't be overridden. The latter may not be bad, the former however, is not good. It's not behaving the way I would expect it to and thus you haven't gained anything that I can see.

    One way to deal with this might be to create two public methods. One is the constructor that sets class data. The other is initialize(), which sets instance data. If the class needs to be subclassed, the subclass only needs to override initialize(). This makes object construction a bit more cumbersome, though.

    my $foo = Foo->new()->initialize(\%data);

    That's not to say that private methods are useless, but I would want to see a more pertinent example before I could comment on how you are using it.

    I'm also wondering about your lines 07 and 08:

    07: my $type = shift; 08: my $class = ref($type) || $type;

    I'm wondering why you have the ref($type) || $type in your code? That statement suggests that you might call new as a class method (Foo->new) and your might call it as an instance method ($foo->new). As a class method I understand, but as an instance method, it behaves as a class method, so why not call it as a class method? Calling it as an instance method can confuse programmers:

    $employee{Sally} = Employee->new; $employee{Bob} = $employee{Sally}->new;

    Without looking at the Employee package, I have no way of knowing what that's doing. Is Sally a really good worker so I decided that Bob is a clone of Sally? If I want to do something like that, I may as well just create a clone method to make the example more clear and to doom Bob to a life of gender confusion (not to mention the fact that the Catholic Church also objects to this practice):

    $employee{Sally} = Employee->new; $employee{Bob} = $employee{Sally}->clone;

    Unless you actually have code that uses the ref $proto || $proto construct, I would leave it out. But when might you need it? When a class method can also be appropriately called as an instance method. For example, I have a program where each object has a mapping of accessor/mutator names to the column names in the database. Thus, I can change the column name, update it in the object's map and I don't have to change the method names. But what happens if an object in a different class needs to get the column name? (for example, when needing dynamically built SQL.) I use the following method:

    sub accessor_to_column { my ($proto,$column) = @_; my $class = ref $proto || $proto; # note the lexical variable to store class data my $found_column = 'id' eq $column ? $CLASS_DATA{$class}{id} : $CLASS_DATA{$class}{accessor_to_column}{$column}; unless ($found_column) { croak "No column found for ($column)"; } return $found_column; }

    With this method, I might need to call it on a class, but I might also be calling it on an object instance. In both cases, the behavior must be the same, so the ref $proto || $proto trick is appropriate.

    Thanks for letting me ramble and I hope I didn't take your code too seriously as an example of what you might actually be inclined to implement.

    Cheers,
    Ovid

    New address of my CGI Course.
    Silence is Evil (feel free to copy and distribute widely - note copyright text)

      I think the printing to STDERR was merely to show us how this works and not a programming technique you actually use, so I'll skip that

      correct.

      I'm more concerned with how you are using $_init_

      Pardon the naming... it was more or less just supposed to be an arbitrarily named private method.

      I'm wondering why you have the ref($type) || $type in your code

      Again, this was just my fingers typing away trying to make a cohesive example... usage for this was not the intended question and how it will be applied to the design is by no way intended to be examplified here.

      So forgive the lacking example. I was merely trying to flesh out the question so that it would not appear vague.


      cp
      ---
      "Never be afraid to try something new. Remember, amateurs built the ark. Professionals built the Titanic."
      However, this means overriding a private method!

      Surely by definition, if you can override a method it isn't private :-)

      That's not to say that private methods are useless, but I would want to see a more pertinent example before I could comment on how you are using it.

      Where "private" methods win is when you want to hide your implementation decisions from subclasses. If you call your method as $self->_whatever you leave yourself open to having your code broken if a subclass adds its own _whatever method.

      You can also break existing code if you add a _something_else method to a later version of your base class when a subclass already has a method of the same name.

      Using lexically scoped coderefs or alternate calling strategies means we can code safe in the knowledge that a subclass is not going to change the intended behaviour of our "private" implementation methods.

        Surely by definition, if you can override a method it isn't private :-)

        Heh :) Perl is less modest about its private parts. When "private" methods are merely a matter of convention, scalability suffers. You bring this up quite nicely with you _something_else example. If Perl's OO was cleaner, fewer programmers would struggle with the subtle bugs of accidentally overriding private methods. Considered in that light, perhaps all private methods should be implemented via coderefs to ensure that this problem just goes away.

        The more I consider OO in Perl, the more I'm inclined to agree with tye's insistence that inheritance should be avoided (at least in Perl).

        Cheers,
        Ovid

        New address of my CGI Course.
        Silence is Evil (feel free to copy and distribute widely - note copyright text)

      This seems to be a highly contested issue with Perl OO programmers. Most feel that private methods should remain private and only the public interface should be exposed (which I now tend to agree with). Others seem to feel that you can override anything you want. If you need to get something done, sometimes the best way is to break the rules.

      Java has class data which can be declared "protected", which means that classes in the same package can access it, but it's private data as far as anyone else is concerned. Protected methods can be overridden. I would say that the subclassible _init() would fall under the catagory of a protected method.

      As is always the case with Perl OO, there are no formal specifications for implementing protected data. Being that Java is the most OO-purist language in common use1, copying it's idioms for OO isn't such a bad thing. It's not like Perl hasn't copied things from everyone else.

      1 Yes, I realize that Java isn't a pure OO language, due to primitives like 'int'. The key phrase here is in common use. My personal theory about this is that truely pure OO langagues are so annoying that no one would want to build a real application with them. (And this is where a bunch of replies come in that tell me how wrong I am and pure OO langs are used all the time :)

      ----
      Reinvent a rounder wheel.

      Note: All code is untested, unless otherwise stated

Re: Private methods
by adrianh (Chancellor) on Mar 09, 2003 at 22:45 UTC

    If you want to create a subroutine that only the Foo package can access, lexically scoped coderefs are the way to go. However, I can only think of a few situations where you really need to be that secretive.

    I find that most of the time all I need is to prevent a subclass overriding the subroutine, in which case I would either calling it as a subroutine rather than a method, or specify the package of the method directly. For example:

    package Foo; sub _init { shift->{foo} = 1 } sub new { my $class = shift; my $self = bless {}, $class; _init($self); $self; } package Bar; use base qw(Foo); sub _init { shift->{bar} = 2 } sub new { my $class = shift; my $self = $class->SUPER::new; _init($self); $self; }

    or

    package Foo; sub _init { shift->{foo} = 1 } sub new { my $class = shift; my $self = bless {}, $class; $self->Foo::_init; $self; } package Bar; use base qw(Foo); sub _init { shift->{bar} = 2 } sub new { my $class = shift; my $self = $class->SUPER::new; $self->Bar::_init; $self; }

    gives us

    package main; use Data::Dumper; print Dumper(Bar->new); __END__ $VAR1 = bless( { 'bar' => 2, 'foo' => 1 }, 'Bar' );

      ... and after Apocalypse 6 we have submethods & BUILD in perl 6:

      ... in Foo ... submethod BUILD { $.foo = 1 }; ... in Bar ... submethod BUILD { $.bar = 2 };

      I so want perl6 now :-)

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (3)
As of 2014-08-02 01:53 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Who would be the most fun to work for?















    Results (53 votes), past polls